messages) {
- if (messages.isEmpty()) {
- return;
+ /**
+ * This class is used to run operations that modify UI elements in the UI thread.
+ *
+ * We are using convenience methods that add a {@link android.os.Message} instance or a
+ * {@link Runnable} to the message queue.
+ *
+ * Note: If you add a method to this class make sure you don't accidentally
+ * 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;
+
+
+ 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);
+ }
+
+ public void folderLoading(String folder, boolean loading) {
+ android.os.Message msg = android.os.Message.obtain(this, ACTION_FOLDER_LOADING,
+ (loading) ? 1 : 0, 0, folder);
+ sendMessage(msg);
+ }
+
+ public void refreshTitle() {
+ android.os.Message msg = android.os.Message.obtain(this, ACTION_REFRESH_TITLE);
+ sendMessage(msg);
+ }
+
+ public void progress(final boolean progress) {
+ android.os.Message msg = android.os.Message.obtain(this, ACTION_PROGRESS,
+ (progress) ? 1 : 0, 0);
+ sendMessage(msg);
+ }
+
+ 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;
+ }
+ case ACTION_FOLDER_LOADING: {
+ String folder = (String) msg.obj;
+ boolean loading = (msg.arg1 == 1);
+ MessageList.this.folderLoading(folder, loading);
+ break;
+ }
+ case ACTION_REFRESH_TITLE: {
+ MessageList.this.refreshTitle();
+ break;
+ }
+ case ACTION_PROGRESS: {
+ boolean progress = (msg.arg1 == 1);
+ MessageList.this.progress(progress);
+ break;
+ }
}
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- for (MessageInfoHolder message : messages) {
- if (message != null && (mFolderName == null || (
- message.folder != null &&
- message.folder.name.equals(mFolderName)))) {
- if (message.selected && mSelectedCount > 0) {
- mSelectedCount--;
- }
- mAdapter.messages.remove(message);
- }
- }
- resetUnreadCountOnThread();
-
- mAdapter.notifyDataSetChanged();
- toggleBatchButtons();
- }
- });
}
+ }
- /**
- * @param messages Never {@code null}.
- */
- public void addMessages(final List messages) {
- if (messages.isEmpty()) {
- return;
- }
- final boolean wasEmpty = mAdapter.messages.isEmpty();
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- for (final MessageInfoHolder message : messages) {
- if (mFolderName == null || (message.folder != null && message.folder.name.equals(mFolderName))) {
- int index;
- synchronized (mAdapter.messages) {
- index = Collections.binarySearch(mAdapter.messages, message, getComparator());
- }
+ /**
+ * @return The comparator to use to display messages in an ordered
+ * fashion. Never null
.
+ */
+ protected Comparator getComparator() {
+ final List> chain = new ArrayList>(2 /* we add 2 comparators at most */);
- if (index < 0) {
- index = (index * -1) - 1;
- }
-
- mAdapter.messages.add(index, message);
- }
- }
-
- if (wasEmpty) {
- mListView.setSelection(0);
- }
- resetUnreadCountOnThread();
-
- mAdapter.notifyDataSetChanged();
- }
- });
- }
-
- private void resetUnreadCount() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- resetUnreadCountOnThread();
- }
- });
- }
-
- private void resetUnreadCountOnThread() {
- if (mQueryString != null) {
- int unreadCount = 0;
- synchronized (mAdapter.messages) {
- for (MessageInfoHolder holder : mAdapter.messages) {
- unreadCount += holder.read ? 0 : 1;
- }
- }
- mUnreadMessageCount = unreadCount;
- refreshTitleOnThread();
+ {
+ // add the specified comparator
+ final Comparator comparator = SORT_COMPARATORS.get(mSortType);
+ if (mSortAscending) {
+ chain.add(comparator);
+ } else {
+ chain.add(new ReverseComparator(comparator));
}
}
- private void sortMessages() {
- final Comparator chainComparator = getComparator();
-
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- synchronized (mAdapter.messages) {
- Collections.sort(mAdapter.messages, chainComparator);
- }
- mAdapter.notifyDataSetChanged();
- }
- });
- }
-
- /**
- * @return The comparator to use to display messages in an ordered
- * fashion. Never null
.
- */
- protected Comparator getComparator() {
- final List> chain = new ArrayList>(2 /* we add 2 comparators at most */);
-
- {
- // add the specified comparator
- final Comparator comparator = SORT_COMPARATORS.get(mSortType);
- if (mSortAscending) {
+ {
+ // add the date comparator if not already specified
+ if (mSortType != SortType.SORT_DATE && mSortType != SortType.SORT_ARRIVAL) {
+ final Comparator comparator = SORT_COMPARATORS.get(SortType.SORT_DATE);
+ if (mSortDateAscending) {
chain.add(comparator);
} else {
chain.add(new ReverseComparator(comparator));
}
}
-
- {
- // add the date comparator if not already specified
- if (mSortType != SortType.SORT_DATE && mSortType != SortType.SORT_ARRIVAL) {
- final Comparator comparator = SORT_COMPARATORS.get(SortType.SORT_DATE);
- if (mSortDateAscending) {
- chain.add(comparator);
- } else {
- chain.add(new ReverseComparator(comparator));
- }
- }
- }
-
- // build the comparator chain
- final Comparator chainComparator = new ComparatorChain(chain);
-
- return chainComparator;
}
- public void folderLoading(String folder, boolean loading) {
- if (mCurrentFolder != null && mCurrentFolder.name.equals(folder)) {
- mCurrentFolder.loading = loading;
- }
- runOnUiThread(new Runnable() {
- @Override public void run() {
- updateFooterView();
- }
- });
+ // build the comparator chain
+ final Comparator chainComparator = new ComparatorChain(chain);
+
+ return chainComparator;
+ }
+
+ private void folderLoading(String folder, boolean loading) {
+ if (mCurrentFolder != null && mCurrentFolder.name.equals(folder)) {
+ mCurrentFolder.loading = loading;
}
+ updateFooterView();
+ }
- private void refreshTitle() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- refreshTitleOnThread();
- }
- });
- }
+ private void refreshTitle() {
+ setWindowTitle();
+ setWindowProgress();
+ }
- private void refreshTitleOnThread() {
- setWindowTitle();
- setWindowProgress();
- }
+ private void setWindowProgress() {
+ int level = Window.PROGRESS_END;
- private void setWindowProgress() {
- int level = Window.PROGRESS_END;
-
- if (mCurrentFolder != null && mCurrentFolder.loading && mAdapter.mListener.getFolderTotal() > 0) {
- int divisor = mAdapter.mListener.getFolderTotal();
- if (divisor != 0) {
- level = (Window.PROGRESS_END / divisor) * (mAdapter.mListener.getFolderCompleted()) ;
- if (level > Window.PROGRESS_END) {
- level = Window.PROGRESS_END;
- }
- }
- }
-
- getWindow().setFeatureInt(Window.FEATURE_PROGRESS, level);
- }
-
- private void setWindowTitle() {
- String displayName;
-
- if (mFolderName != null) {
- displayName = mFolderName;
-
- if (mAccount.getInboxFolderName().equalsIgnoreCase(displayName)) {
- displayName = getString(R.string.special_mailbox_name_inbox);
- } else if (mAccount.getOutboxFolderName().equals(displayName)) {
- displayName = getString(R.string.special_mailbox_name_outbox);
- }
-
- String dispString = mAdapter.mListener.formatHeader(MessageList.this, getString(R.string.message_list_title, mAccount.getDescription(), displayName), mUnreadMessageCount, getTimeFormat());
- setTitle(dispString);
- } else if (mQueryString != null) {
- if (mTitle != null) {
- String dispString = mAdapter.mListener.formatHeader(MessageList.this, mTitle, mUnreadMessageCount, getTimeFormat());
- setTitle(dispString);
- } else {
- setTitle(getString(R.string.search_results) + ": " + mQueryString);
+ if (mCurrentFolder != null && mCurrentFolder.loading && mAdapter.mListener.getFolderTotal() > 0) {
+ int divisor = mAdapter.mListener.getFolderTotal();
+ if (divisor != 0) {
+ level = (Window.PROGRESS_END / divisor) * (mAdapter.mListener.getFolderCompleted()) ;
+ if (level > Window.PROGRESS_END) {
+ level = Window.PROGRESS_END;
}
}
}
- public void progress(final boolean progress) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- showProgressIndicator(progress);
- }
- });
+ setSupportProgress(level);
+ }
+
+ private void setWindowTitle() {
+ // regular folder content display
+ if (mFolderName != null) {
+ String displayName = FolderInfoHolder.getDisplayName(MessageList.this, mAccount,
+ mFolderName);
+
+ mActionBarTitle.setText(displayName);
+
+ String operation = mAdapter.mListener.getOperation(MessageList.this, getTimeFormat()).trim();
+ if (operation.length() < 1) {
+ mActionBarSubTitle.setText(mAccount.getEmail());
+ } else {
+ mActionBarSubTitle.setText(operation);
+ }
+ } else if (mQueryString != null) {
+ // query result display. This may be for a search folder as opposed to a user-initiated search.
+ if (mTitle != null) {
+ // This was a search folder; the search folder has overridden our title.
+ mActionBarTitle.setText(mTitle);
+ } else {
+ // This is a search result; set it to the default search result line.
+ mActionBarTitle.setText(getString(R.string.search_results));
+ }
+ }
+
+ // set unread count
+ if (mUnreadMessageCount == 0) {
+ mActionBarUnread.setVisibility(View.GONE);
+ } else {
+ if (mQueryString != null && mTitle == null) {
+ // This is a search result. The unread message count is easily confused
+ // with total number of messages in the search result, so let's hide it.
+ mActionBarUnread.setVisibility(View.GONE);
+ } else {
+ mActionBarUnread.setText(Integer.toString(mUnreadMessageCount));
+ mActionBarUnread.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ private void progress(final boolean progress) {
+ // Make sure we don't try this before the menu is initialized
+ // this could happen while the activity is initialized.
+ if (mRefreshMenuItem == null) {
+ return;
+ }
+
+ if (progress) {
+ mRefreshMenuItem.setActionView(mActionBarProgressView);
+ } else {
+ mRefreshMenuItem.setActionView(null);
}
}
@@ -581,24 +584,27 @@ public class MessageList
*/
public static void actionHandleFolder(Context context, Bundle extras) {
Intent intent = new Intent(context, MessageList.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtras(extras);
intent.putExtra(EXTRA_RETURN_FROM_MESSAGE_VIEW, true);
context.startActivity(intent);
}
public static void actionHandleFolder(Context context, Account account, String folder) {
- Intent intent = actionHandleFolderIntent(context, account, folder);
+ Intent intent = new Intent(context, MessageList.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.putExtra(EXTRA_ACCOUNT, account.getUuid());
+
+ if (folder != null) {
+ intent.putExtra(EXTRA_FOLDER, folder);
+ }
context.startActivity(intent);
}
public static Intent actionHandleFolderIntent(Context context, Account account, String folder) {
Intent intent = new Intent(context, MessageList.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP |
+ Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(EXTRA_ACCOUNT, account.getUuid());
if (folder != null) {
@@ -609,6 +615,7 @@ public class MessageList
public static void actionHandle(Context context, String title, String queryString, boolean integrate, Flag[] flags, Flag[] forbiddenFlags) {
Intent intent = new Intent(context, MessageList.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(EXTRA_QUERY, queryString);
if (flags != null) {
intent.putExtra(EXTRA_QUERY_FLAGS, Utility.combine(flags, ','));
@@ -618,9 +625,6 @@ public class MessageList
}
intent.putExtra(EXTRA_INTEGRATE, integrate);
intent.putExtra(EXTRA_TITLE, title);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent);
}
@@ -649,7 +653,7 @@ public class MessageList
}
public static void actionHandle(Context context, String title,
- SearchSpecification searchSpecification) {
+ SearchSpecification searchSpecification) {
Intent intent = actionHandleAccountIntent(context, title, searchSpecification);
context.startActivity(intent);
}
@@ -665,9 +669,7 @@ public class MessageList
MessageInfoHolder message = (MessageInfoHolder) mAdapter.getItem(position);
if (mSelectedCount > 0) {
- // In multiselect mode make sure that clicking on the item results
- // in toggling the 'selected' checkbox.
- setSelected(Collections.singletonList(message), !message.selected);
+ handleContextRelatedClick(position);
} else {
onOpenMessage(message);
}
@@ -678,18 +680,37 @@ public class MessageList
context = this;
super.onCreate(savedInstanceState);
+ mActionBarProgressView = getLayoutInflater().inflate(R.layout.actionbar_indeterminate_progress_actionview, null);
+
+ // need this for actionbar initialization
+ mQueryString = getIntent().getStringExtra(EXTRA_QUERY);
+
mInflater = getLayoutInflater();
+ mActionBar = getSupportActionBar();
+ initializeActionBar();
initializeLayout();
- // Only set "touchable" when we're first starting up the activity.
- // Otherwise we get force closes when the user toggles it midstream.
- mTouchView = K9.messageListTouchable();
mPreviewLines = K9.messageListPreviewLines();
initializeMessageList(getIntent(), true);
+ getListView().setVerticalFadingEdgeEnabled(false);
- // Enable gesture detection for MessageLists
- mGestureDetector = new GestureDetector(new MyGestureDetector(true));
+ // Enable context action bar behaviour
+ getListView().setOnItemLongClickListener(new OnItemLongClickListener() {
+ @Override
+ public boolean onItemLongClick(AdapterView> parent, View view,
+ int position, long id) {
+ return handleContextRelatedClick(position);
+ }});
+
+ // Correcting for screen rotation when in ActionMode
+ mSelectedCount = getSelectionFromCheckboxes().size();
+ if (mSelectedCount > 0) {
+ mActionMode = MessageList.this.startActionMode(mActionModeCallback);
+ mActionMode.setTitle(String.format(
+ getString(R.string.actionbar_selected),
+ mSelectedCount));
+ }
}
@Override
@@ -700,7 +721,7 @@ public class MessageList
private void initializeMessageList(Intent intent, boolean create) {
boolean returnFromMessageView = intent.getBooleanExtra(
- EXTRA_RETURN_FROM_MESSAGE_VIEW, false);
+ EXTRA_RETURN_FROM_MESSAGE_VIEW, false);
if (!create && returnFromMessageView) {
// We're returning from the MessageView activity with "Manage back button" enabled.
@@ -773,7 +794,7 @@ public class MessageList
final ActivityState previousData = getLastNonConfigurationInstance();
if (previousData != null) {
- mAdapter.messages.addAll(previousData.messages);
+ mAdapter.restoreMessages(previousData.messages);
mActiveMessages = previousData.activeMessages;
}
}
@@ -851,39 +872,38 @@ public class MessageList
mController.notifyAccountCancel(this, accountWithNotification);
}
- if (mAdapter.messages.isEmpty()) {
+ if (mAdapter.isEmpty()) {
if (mFolderName != null) {
mController.listLocalMessages(mAccount, mFolderName, mAdapter.mListener);
// Hide the archive button if we don't have an archive folder.
if (!mAccount.hasArchiveFolder()) {
- mBatchArchiveButton.setVisibility(View.GONE);
+// mBatchArchiveButton.setVisibility(View.GONE);
}
} else if (mQueryString != null) {
mController.searchLocalMessages(mAccountUuids, mFolderNames, null, mQueryString, mIntegrate, mQueryFlags, mForbiddenFlags, mAdapter.mListener);
// Don't show the archive button if this is a search.
- mBatchArchiveButton.setVisibility(View.GONE);
+// mBatchArchiveButton.setVisibility(View.GONE);
}
} else {
// reread the selected date format preference in case it has changed
mMessageHelper.refresh();
+ mAdapter.markAllMessagesAsDirty();
+
new Thread() {
@Override
public void run() {
- mAdapter.markAllMessagesAsDirty();
-
if (mFolderName != null) {
mController.listLocalMessagesSynchronous(mAccount, mFolderName, mAdapter.mListener);
} else if (mQueryString != null) {
mController.searchLocalMessagesSynchronous(mAccountUuids, mFolderNames, null, mQueryString, mIntegrate, mQueryFlags, mForbiddenFlags, mAdapter.mListener);
}
-
- mAdapter.pruneDirtyMessages();
runOnUiThread(new Runnable() {
@Override
public void run() {
+ mAdapter.pruneDirtyMessages();
mAdapter.notifyDataSetChanged();
restoreListState();
}
@@ -897,15 +917,29 @@ public class MessageList
if (mAccount != null && mFolderName != null) {
mController.getFolderUnreadMessageCount(mAccount, mFolderName, mAdapter.mListener);
}
- mHandler.refreshTitle();
+ refreshTitle();
}
- private void initializeLayout() {
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- requestWindowFeature(Window.FEATURE_PROGRESS);
- setContentView(R.layout.message_list);
- mListView = (ListView) findViewById(R.id.message_list);
+ private void initializeActionBar() {
+ mActionBar.setDisplayShowCustomEnabled(true);
+ mActionBar.setCustomView(R.layout.actionbar_custom);
+
+ View customView = mActionBar.getCustomView();
+ mActionBarTitle = (TextView) customView.findViewById(R.id.actionbar_title_first);
+ mActionBarSubTitle = (TextView) customView.findViewById(R.id.actionbar_title_sub);
+ mActionBarUnread = (TextView) customView.findViewById(R.id.actionbar_unread_count);
+
+ if (mQueryString != null) {
+ mActionBarSubTitle.setVisibility(View.GONE);
+ }
+
+ mActionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+ private void initializeLayout() {
+ mListView = getListView();
+ mListView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
mListView.setLongClickable(true);
mListView.setFastScrollEnabled(true);
mListView.setScrollingCacheEnabled(false);
@@ -913,29 +947,9 @@ public class MessageList
mListView.addFooterView(getFooterView(mListView));
registerForContextMenu(mListView);
-
- mBatchButtonArea = findViewById(R.id.batch_button_area);
- mBatchReadButton = (ImageButton) findViewById(R.id.batch_read_button);
- mBatchReadButton.setOnClickListener(this);
- mBatchDeleteButton = (ImageButton) findViewById(R.id.batch_delete_button);
- mBatchDeleteButton.setOnClickListener(this);
- mBatchFlagButton = (ImageButton) findViewById(R.id.batch_flag_button);
- mBatchFlagButton.setOnClickListener(this);
- mBatchArchiveButton = (ImageButton) findViewById(R.id.batch_archive_button);
- mBatchArchiveButton.setOnClickListener(this);
- mBatchMoveButton = (ImageButton) findViewById(R.id.batch_move_button);
- mBatchMoveButton.setOnClickListener(this);
- mBatchDoneButton = (ImageButton) findViewById(R.id.batch_done_button);
- mBatchDoneButton.setOnClickListener(this);
-
- mBatchReadButton.setVisibility(K9.batchButtonsMarkRead() ? View.VISIBLE : View.GONE);
- mBatchDeleteButton.setVisibility(K9.batchButtonsDelete() ? View.VISIBLE : View.GONE);
- mBatchArchiveButton.setVisibility(K9.batchButtonsArchive() ? View.VISIBLE : View.GONE);
- mBatchMoveButton.setVisibility(K9.batchButtonsMove() ? View.VISIBLE : View.GONE);
- mBatchFlagButton.setVisibility(K9.batchButtonsFlag() ? View.VISIBLE : View.GONE);
- mBatchDoneButton.setVisibility(K9.batchButtonsUnselect() ? View.VISIBLE : View.GONE);
}
+
/**
* Container for values to be kept while the device configuration is
* modified at runtime (keyboard, orientation, etc.) and Android restarts
@@ -959,7 +973,7 @@ public class MessageList
@Override
public ActivityState onRetainNonConfigurationInstance() {
final ActivityState state = new ActivityState();
- state.messages = mAdapter.messages;
+ state.messages = mAdapter.getMessages();
state.activeMessages = mActiveMessages;
return state;
}
@@ -977,19 +991,6 @@ public class MessageList
return (ActivityState) super.getLastNonConfigurationInstance();
}
- @Override
- public void onBackPressed() {
- if (K9.manageBack()) {
- if (mQueryString == null) {
- onShowFolderList();
- } else {
- onAccounts();
- }
- } else {
- super.onBackPressed();
- }
- }
-
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Shortcuts that work no matter what is selected
@@ -1025,20 +1026,6 @@ public class MessageList
}
return false;
}
- case KeyEvent.KEYCODE_DPAD_LEFT: {
- if (mBatchButtonArea.hasFocus()) {
- return false;
- } else {
- return true;
- }
- }
- case KeyEvent.KEYCODE_DPAD_RIGHT: {
- if (mBatchButtonArea.hasFocus()) {
- return false;
- } else {
- return true;
- }
- }
case KeyEvent.KEYCODE_C: {
onCompose();
return true;
@@ -1084,18 +1071,6 @@ public class MessageList
onDelete(selection);
return true;
}
- case KeyEvent.KEYCODE_F: {
- onForward(message);
- return true;
- }
- case KeyEvent.KEYCODE_A: {
- onReplyAll(message);
- return true;
- }
- case KeyEvent.KEYCODE_R: {
- onReply(message);
- return true;
- }
case KeyEvent.KEYCODE_G: {
setFlag(selection, Flag.FLAGGED, !message.flagged);
return true;
@@ -1138,11 +1113,6 @@ public class MessageList
return super.onKeyUp(keyCode, event);
}
-
- private void onResendMessage(MessageInfoHolder message) {
- MessageCompose.actionEditDraft(this, message.message.getFolder().getAccount(), message.message);
- }
-
private void onOpenMessage(MessageInfoHolder message) {
if (message.folder.name.equals(message.message.getFolder().getAccount().getDraftsFolderName())) {
MessageCompose.actionEditDraft(this, message.message.getFolder().getAccount(), message.message);
@@ -1150,12 +1120,11 @@ public class MessageList
// Need to get the list before the sort starts
ArrayList messageRefs = new ArrayList();
- synchronized (mAdapter.messages) {
- for (MessageInfoHolder holder : mAdapter.messages) {
- MessageReference ref = holder.message.makeMessageReference();
- messageRefs.add(ref);
- }
+ for (MessageInfoHolder holder : mAdapter.getMessages()) {
+ MessageReference ref = holder.message.makeMessageReference();
+ messageRefs.add(ref);
}
+
MessageReference ref = message.message.makeMessageReference();
Log.i(K9.LOG_TAG, "MessageList sending message " + ref);
@@ -1196,6 +1165,22 @@ public class MessageList
}
}
+ private void onReply(MessageInfoHolder holder) {
+ MessageCompose.actionReply(this, holder.message.getFolder().getAccount(), holder.message, false, null);
+ }
+
+ private void onReplyAll(MessageInfoHolder holder) {
+ MessageCompose.actionReply(this, holder.message.getFolder().getAccount(), holder.message, true, null);
+ }
+
+ private void onForward(MessageInfoHolder holder) {
+ MessageCompose.actionForward(this, holder.message.getFolder().getAccount(), holder.message, null);
+ }
+
+ private void onResendMessage(MessageInfoHolder message) {
+ MessageCompose.actionEditDraft(this, message.message.getFolder().getAccount(), message.message);
+ }
+
private void onEditPrefs() {
Prefs.actionPrefs(this);
}
@@ -1262,7 +1247,7 @@ public class MessageList
Toast toast = Toast.makeText(this, toastString, Toast.LENGTH_SHORT);
toast.show();
- mHandler.sortMessages();
+ mAdapter.sortMessages();
}
private void onCycleSort() {
@@ -1294,7 +1279,7 @@ public class MessageList
for (MessageInfoHolder holder : holders) {
messagesToRemove.add(holder.message);
}
- mHandler.removeMessages(holders);
+ mAdapter.removeMessages(holders);
mController.deleteMessages(messagesToRemove.toArray(EMPTY_MESSAGE_ARRAY), null);
}
@@ -1312,9 +1297,9 @@ public class MessageList
}
final String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER);
+ final List holders = mActiveMessages;
if (destFolderName != null) {
- final List holders = mActiveMessages;
mActiveMessages = null; // don't need it any more
@@ -1336,18 +1321,6 @@ public class MessageList
}
}
- private void onReply(MessageInfoHolder holder) {
- MessageCompose.actionReply(this, holder.message.getFolder().getAccount(), holder.message, false, null);
- }
-
- private void onReplyAll(MessageInfoHolder holder) {
- MessageCompose.actionReply(this, holder.message.getFolder().getAccount(), holder.message, true, null);
- }
-
- private void onForward(MessageInfoHolder holder) {
- MessageCompose.actionForward(this, holder.message.getFolder().getAccount(), holder.message, null);
- }
-
private void onMarkAllAsRead(final Account account, final String folder) {
if (K9.confirmMarkAllAsRead()) {
showDialog(DIALOG_MARK_ALL_AS_READ);
@@ -1360,12 +1333,10 @@ public class MessageList
try {
mController.markAllMessagesRead(mAccount, mCurrentFolder.name);
- synchronized (mAdapter.messages) {
- for (MessageInfoHolder holder : mAdapter.messages) {
- holder.read = true;
- }
+ for (MessageInfoHolder holder : mAdapter.getMessages()) {
+ holder.read = true;
}
- mHandler.sortMessages();
+ mAdapter.sortMessages();
} catch (Exception e) {
// Ignore
}
@@ -1453,24 +1424,46 @@ public class MessageList
}
}
- private void onToggleRead(MessageInfoHolder holder) {
- LocalMessage message = holder.message;
- Folder folder = message.getFolder();
- Account account = folder.getAccount();
- String folderName = folder.getName();
- mController.setFlag(account, folderName, new Message[] { message }, Flag.SEEN, !holder.read);
- holder.read = !holder.read;
- mHandler.sortMessages();
+ private void onToggleRead(final List holders) {
+ LocalMessage message;
+ Folder folder;
+ Account account;
+ String folderName;
+
+ int i = 0;
+ for (final Iterator iterator = holders.iterator(); iterator.hasNext(); i++) {
+ final MessageInfoHolder messageInfo = iterator.next();
+ message = messageInfo.message;
+ folder = message.getFolder();
+ account = folder.getAccount();
+ folderName = message.getFolder().getName();
+
+ mController.setFlag(account, folderName, new Message[]{message}, Flag.SEEN, !messageInfo.read);
+
+ messageInfo.read = !messageInfo.read;
+ mAdapter.sortMessages();
+ }
}
- private void onToggleFlag(MessageInfoHolder holder) {
- LocalMessage message = holder.message;
- Folder folder = message.getFolder();
- Account account = folder.getAccount();
- String folderName = folder.getName();
- mController.setFlag(account, folderName, new Message[] { message }, Flag.FLAGGED, !holder.flagged);
- holder.flagged = !holder.flagged;
- mHandler.sortMessages();
+ private void onToggleFlag(final List holders) {
+ LocalMessage message;
+ Folder folder;
+ Account account;
+ String folderName;
+
+ int i = 0;
+ for (final Iterator iterator = holders.iterator(); iterator.hasNext(); i++) {
+ final MessageInfoHolder messageInfo = iterator.next();
+ message = messageInfo.message;
+ folder = message.getFolder();
+ account = folder.getAccount();
+ folderName = message.getFolder().getName();
+
+ mController.setFlag(account, folderName, new Message[]{message}, Flag.FLAGGED, !messageInfo.flagged);
+
+ messageInfo.flagged = !messageInfo.flagged;
+ mAdapter.sortMessages();
+ }
}
private void checkMail(Account account, String folderName) {
@@ -1480,16 +1473,23 @@ public class MessageList
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- final List selection = getSelectionFromCheckboxes();
int itemId = item.getItemId();
switch (itemId) {
+ case android.R.id.home: {
+ if (mQueryString == null) {
+ onShowFolderList();
+ } else {
+ onAccounts();
+ }
+ return true;
+ }
case R.id.compose: {
onCompose();
return true;
}
- case R.id.accounts: {
- onAccounts();
- return true;
+ case R.id.check_mail: {
+ checkMail(mAccount, mFolderName);
+ return true;
}
case R.id.set_sort_date: {
changeSort(SortType.SORT_DATE);
@@ -1520,36 +1520,8 @@ public class MessageList
return true;
}
case R.id.select_all:
- case R.id.batch_select_all: {
- setAllSelected(true);
- toggleBatchButtons();
- return true;
- }
- case R.id.batch_deselect_all: {
- setAllSelected(false);
- toggleBatchButtons();
- return true;
- }
- case R.id.batch_delete_op: {
- onDelete(selection);
- return true;
- }
- case R.id.batch_mark_read_op: {
- setFlag(selection, Flag.SEEN, true);
- return true;
- }
- case R.id.batch_mark_unread_op: {
- setFlag(selection, Flag.SEEN, false);
- return true;
- }
- case R.id.batch_flag_op: {
- setFlag(selection, Flag.FLAGGED, true);
- return true;
- }
- case R.id.batch_unflag_op: {
- setFlag(selection, Flag.FLAGGED, false);
- return true;
- }
+ toggleAllSelected();
+ return true;
case R.id.app_settings: {
onEditPrefs();
return true;
@@ -1563,20 +1535,10 @@ public class MessageList
}
switch (itemId) {
- case R.id.check_mail: {
- if (mFolderName != null) {
- checkMail(mAccount, mFolderName);
- }
- return true;
- }
case R.id.send_messages: {
mController.sendPendingMessages(mAccount, mAdapter.mListener);
return true;
}
- case R.id.list_folders: {
- onShowFolderList();
- return true;
- }
case R.id.mark_all_as_read: {
if (mFolderName != null) {
onMarkAllAsRead(mAccount, mFolderName);
@@ -1593,26 +1555,11 @@ public class MessageList
onEditAccount();
return true;
}
- case R.id.batch_copy_op: {
- onCopy(selection);
- return true;
- }
- case R.id.batch_archive_op: {
- onArchive(selection);
- return true;
- }
- case R.id.batch_spam_op: {
- onSpam(selection);
- return true;
- }
- case R.id.batch_move_op: {
- onMove(selection);
- return true;
- }
- case R.id.batch_upload_op: {
- onUpload(selection);
- return true;
- }
+ // ASH the other batch things were removed
+ //case R.id.batch_upload_op: {
+ // onUpload(selection);
+ // return true;
+ //}
case R.id.expunge: {
if (mCurrentFolder != null) {
onExpunge(mAccount, mCurrentFolder.name);
@@ -1625,37 +1572,21 @@ public class MessageList
}
}
- private final int[] batch_ops = { R.id.batch_copy_op, R.id.batch_delete_op, R.id.batch_flag_op,
- R.id.batch_unflag_op, R.id.batch_mark_read_op, R.id.batch_mark_unread_op,
- R.id.batch_archive_op, R.id.batch_spam_op, R.id.batch_move_op,
- R.id.batch_select_all, R.id.batch_deselect_all
- };
-
- private void setOpsState(Menu menu, boolean state, boolean enabled) {
- for (int id : batch_ops) {
- menu.findItem(id).setVisible(state);
- menu.findItem(id).setEnabled(enabled);
- }
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getSupportMenuInflater().inflate(R.menu.message_list_option, menu);
+ mRefreshMenuItem = menu.findItem(R.id.check_mail);
+ return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
- boolean anySelected = anySelected();
-
- menu.findItem(R.id.select_all).setVisible(! anySelected);
- menu.findItem(R.id.batch_ops).setVisible(anySelected);
-
- setOpsState(menu, true, anySelected);
if (mQueryString != null) {
menu.findItem(R.id.mark_all_as_read).setVisible(false);
- menu.findItem(R.id.list_folders).setVisible(false);
menu.findItem(R.id.expunge).setVisible(false);
- menu.findItem(R.id.batch_archive_op).setVisible(false);
- menu.findItem(R.id.batch_spam_op).setVisible(false);
- menu.findItem(R.id.batch_move_op).setVisible(false);
- menu.findItem(R.id.batch_copy_op).setVisible(false);
- menu.findItem(R.id.batch_upload_op).setVisible(false);
+ //menu.findItem(R.id.batch_upload_op).setVisible(false); // ASH the other batch options were removed
menu.findItem(R.id.check_mail).setVisible(false);
menu.findItem(R.id.send_messages).setVisible(false);
menu.findItem(R.id.folder_settings).setVisible(false);
@@ -1679,12 +1610,6 @@ public class MessageList
mLocalOnly || !isExpungeCapable) {
menu.findItem(R.id.expunge).setVisible(false);
}
- if (!mAccount.hasArchiveFolder()) {
- menu.findItem(R.id.batch_archive_op).setVisible(false);
- }
- if (!mAccount.hasSpamFolder()) {
- menu.findItem(R.id.batch_spam_op).setVisible(false);
- }
if (!mController.isMoveCapable(mAccount)) {
// FIXME: Really we want to do this for all local-only folders
@@ -1692,12 +1617,9 @@ public class MessageList
!mAccount.getInboxFolderName().equals(mCurrentFolder.name)) {
menu.findItem(R.id.check_mail).setVisible(false);
}
- menu.findItem(R.id.batch_archive_op).setVisible(false);
- menu.findItem(R.id.batch_spam_op).setVisible(false);
- menu.findItem(R.id.batch_move_op).setVisible(false);
- menu.findItem(R.id.batch_copy_op).setVisible(false);
menu.findItem(R.id.expunge).setVisible(false);
}
+ /* ASH
try {
if (((com.fsck.k9.mail.store.LocalStore.LocalFolder)mCurrentFolder.folder).isLocalOnly() ||
!mAccount.getRemoteStore().isAppendCapable()) {
@@ -1705,222 +1627,15 @@ public class MessageList
}
} catch (com.fsck.k9.mail.MessagingException e) {
Log.e(K9.LOG_TAG, "Error trying to get remote store: " + e);
- }
+ }*/
}
- boolean newFlagState = computeBatchDirection(true);
- boolean newReadState = computeBatchDirection(false);
- menu.findItem(R.id.batch_flag_op).setVisible(newFlagState);
- menu.findItem(R.id.batch_unflag_op).setVisible(!newFlagState);
- menu.findItem(R.id.batch_mark_read_op).setVisible(newReadState);
- menu.findItem(R.id.batch_mark_unread_op).setVisible(!newReadState);
- menu.findItem(R.id.batch_deselect_all).setVisible(anySelected);
- menu.findItem(R.id.batch_select_all).setEnabled(true);
-
return true;
}
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.message_list_option, menu);
-
- return true;
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
- final MessageInfoHolder holder = mSelectedMessage == null ? (MessageInfoHolder) mAdapter.getItem(info.position) : mSelectedMessage;
- // don't need this anymore
- mSelectedMessage = null;
-
- final List selection = getSelectionFromMessage(holder);
- switch (item.getItemId()) {
- case R.id.open: {
- onOpenMessage(holder);
- break;
- }
- case R.id.select: {
- setSelected(selection, true);
- break;
- }
- case R.id.deselect: {
- setSelected(selection, false);
- break;
- }
- case R.id.delete: {
- onDelete(selection);
- break;
- }
- case R.id.reply: {
- onReply(holder);
- break;
- }
- case R.id.reply_all: {
- onReplyAll(holder);
- break;
- }
- case R.id.forward: {
- onForward(holder);
- break;
- }
- case R.id.send_again: {
- onResendMessage(holder);
- break;
-
- }
- case R.id.mark_as_read: {
- onToggleRead(holder);
- break;
- }
- case R.id.flag: {
- onToggleFlag(holder);
- break;
- }
- 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;
- }
- case R.id.upload: {
- onUpload(selection);
- break;
- }
- case R.id.send_alternate: {
- onSendAlternate(mAccount, holder);
- break;
- }
- case R.id.same_sender: {
- MessageList.actionHandle(MessageList.this,
- "From " + holder.sender, holder.senderAddress, false,
- null, null);
- break;
- }
- }
- return super.onContextItemSelected(item);
- }
-
- public void onSendAlternate(Account account, MessageInfoHolder holder) {
- mController.sendAlternate(this, account, holder.message);
- }
-
- public void showProgressIndicator(boolean status) {
- setProgressBarIndeterminateVisibility(status);
- ProgressBar bar = (ProgressBar)mListView.findViewById(R.id.message_list_progress);
- if (bar == null) {
- return;
- }
-
- bar.setIndeterminate(true);
- if (status) {
- bar.setVisibility(ProgressBar.VISIBLE);
- } else {
- bar.setVisibility(ProgressBar.INVISIBLE);
- }
- }
-
- @Override
- protected void onSwipeRightToLeft(final MotionEvent e1, final MotionEvent e2) {
- // Handle right-to-left as an un-select
- handleSwipe(e1, false);
- }
-
- @Override
- protected void onSwipeLeftToRight(final MotionEvent e1, final MotionEvent e2) {
- // Handle left-to-right as a select.
- handleSwipe(e1, true);
- }
-
- /**
- * Handle a select or unselect swipe event
- * @param downMotion Event that started the swipe
- * @param selected true if this was an attempt to select (i.e. left to right).
- */
- 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) {
- MessageInfoHolder msgInfoHolder = (MessageInfoHolder) mAdapter.getItem(position);
-
- if (msgInfoHolder != null && msgInfoHolder.selected != selected) {
- msgInfoHolder.selected = selected;
- mSelectedCount += (selected ? 1 : -1);
- mAdapter.notifyDataSetChanged();
- toggleBatchButtons();
- }
- }
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
-
- AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
- MessageInfoHolder message = (MessageInfoHolder) mAdapter.getItem(info.position);
- // remember which message was originally selected, in case the list changes while the
- // dialog is up
- mSelectedMessage = message;
-
- if (message == null) {
- return;
- }
-
- getMenuInflater().inflate(R.menu.message_list_context, menu);
-
- menu.setHeaderTitle(message.message.getSubject());
-
- if (message.read) {
- menu.findItem(R.id.mark_as_read).setTitle(R.string.mark_as_unread_action);
- }
-
- if (message.flagged) {
- menu.findItem(R.id.flag).setTitle(R.string.unflag_action);
- }
-
- Account account = message.message.getFolder().getAccount();
-
- if (!account.hasArchiveFolder()) {
- menu.findItem(R.id.archive).setVisible(false);
- }
-
- if (!account.hasSpamFolder()) {
- menu.findItem(R.id.spam).setVisible(false);
- }
-
- try {
- if (!message.uid.startsWith(K9.LOCAL_UID_PREFIX) || mQueryString != null ||
- ((com.fsck.k9.mail.store.LocalStore.LocalFolder)message.message.getFolder()).isLocalOnly() ||
- !account.getRemoteStore().isAppendCapable()) {
- menu.findItem(R.id.upload).setVisible(false);
- }
- } catch (com.fsck.k9.mail.MessagingException e) {
- Log.e(K9.LOG_TAG, "Error trying to get remote store: " + e);
- }
-
- if (message.selected) {
- menu.findItem(R.id.select).setVisible(false);
- menu.findItem(R.id.deselect).setVisible(true);
- } else {
- menu.findItem(R.id.select).setVisible(true);
- menu.findItem(R.id.deselect).setVisible(false);
- }
- }
-
class MessageListAdapter extends BaseAdapter {
- private final List messages = java.util.Collections.synchronizedList(new ArrayList());
+ private final List mMessages =
+ Collections.synchronizedList(new ArrayList());
private final ActivityListener mListener = new ActivityListener() {
@@ -1963,17 +1678,12 @@ public class MessageList
@Override
public void synchronizeMailboxAddOrUpdateMessage(Account account, String folder, Message message) {
- addOrUpdateMessage(account, folder, message, true);
+ mHandler.addOrUpdateMessages(account, folder, Collections.singletonList(message), true);
}
@Override
public void synchronizeMailboxRemovedMessage(Account account, String folder, Message message) {
- MessageInfoHolder holder = getMessage(message);
- if (holder == null) {
- Log.w(K9.LOG_TAG, "Got callback to remove non-existent message with UID " + message.getUid());
- } else {
- removeMessages(Collections.singletonList(holder));
- }
+ mHandler.removeMessage(message.makeMessageReference());
}
@Override
@@ -2010,20 +1720,17 @@ public class MessageList
@Override
public void listLocalMessagesRemoveMessage(Account account, String folder, Message message) {
- MessageInfoHolder holder = getMessage(message);
- if (holder != null) {
- removeMessages(Collections.singletonList(holder));
- }
+ mHandler.removeMessage(message.makeMessageReference());
}
@Override
public void listLocalMessagesAddMessages(Account account, String folder, List messages) {
- addOrUpdateMessages(account, folder, messages, false);
+ mHandler.addOrUpdateMessages(account, folder, messages, false);
}
@Override
public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {
- addOrUpdateMessage(account, folder, message, false);
+ mHandler.addOrUpdateMessages(account, folder, Collections.singletonList(message), false);
}
@Override
@@ -2047,11 +1754,7 @@ public class MessageList
ref.folderName = folder;
ref.uid = oldUid;
- MessageInfoHolder holder = getMessage(ref);
- if (holder != null) {
- holder.uid = newUid;
- holder.message.setUid(newUid);
- }
+ mHandler.changeMessageUid(ref, newUid);
}
};
@@ -2063,6 +1766,14 @@ public class MessageList
}
}
+ public List getMessages() {
+ return mMessages;
+ }
+
+ public void restoreMessages(List messages) {
+ mMessages.addAll(messages);
+ }
+
private Drawable mAttachmentIcon;
private Drawable mForwardedIcon;
private Drawable mAnsweredIcon;
@@ -2076,44 +1787,122 @@ public class MessageList
}
public void markAllMessagesAsDirty() {
- for (MessageInfoHolder holder : mAdapter.messages) {
+ for (MessageInfoHolder holder : mMessages) {
holder.dirty = true;
}
}
+
public void pruneDirtyMessages() {
- synchronized (mAdapter.messages) {
- Iterator iter = mAdapter.messages.iterator();
- while (iter.hasNext()) {
- MessageInfoHolder holder = iter.next();
- if (holder.dirty) {
- if (holder.selected) {
- mSelectedCount--;
- toggleBatchButtons();
- }
- mAdapter.removeMessages(Collections.singletonList(holder));
- }
+ 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();
}
/**
- * @param holders
- * Never {@code null}.
+ * Set the selection state for all messages at once.
+ * @param selected Selection state to set.
*/
- public void removeMessages(List holders) {
- mHandler.removeMessages(holders);
+ public void setSelectionForAllMesages(final boolean selected) {
+ for (MessageInfoHolder message : mMessages) {
+ message.selected = selected;
+ }
+
+ notifyDataSetChanged();
}
- private void addOrUpdateMessage(Account account, String folderName, Message message, boolean verifyAgainstSearch) {
- List messages = new ArrayList();
- messages.add(message);
- addOrUpdateMessages(account, folderName, messages, verifyAgainstSearch);
+ 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();
}
- private void addOrUpdateMessages(final Account account, final String folderName, final List providedMessages, final boolean verifyAgainstSearch) {
- // we copy the message list because the callback doesn't expect
- // the callbacks to mutate it.
- final List messages = new ArrayList(providedMessages);
+ 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();
+ }
+ }
+
+ public void sortMessages() {
+ final Comparator chainComparator = getComparator();
+
+ Collections.sort(mMessages, chainComparator);
+
+ notifyDataSetChanged();
+ }
+
+ public void addOrUpdateMessages(final Account account, final String folderName,
+ final List messages, final boolean verifyAgainstSearch) {
boolean needsSort = false;
final List messagesToAdd = new ArrayList();
@@ -2135,7 +1924,9 @@ public class MessageList
if (m == null) {
if (updateForMe(account, folderName)) {
m = new MessageInfoHolder();
- messageHelper.populate(m, message, new FolderInfoHolder(MessageList.this, messageFolder, messageAccount), messageAccount);
+ FolderInfoHolder folderInfoHolder = new FolderInfoHolder(
+ MessageList.this, messageFolder, messageAccount);
+ messageHelper.populate(m, message, folderInfoHolder, messageAccount);
messagesToAdd.add(m);
} else {
if (mQueryString != null) {
@@ -2143,25 +1934,33 @@ public class MessageList
messagesToSearch.add(message);
} else {
m = new MessageInfoHolder();
- messageHelper.populate(m, message, new FolderInfoHolder(MessageList.this, messageFolder, messageAccount), messageAccount);
+ FolderInfoHolder folderInfoHolder = new FolderInfoHolder(
+ MessageList.this, messageFolder, messageAccount);
+ messageHelper.populate(m, message, folderInfoHolder,
+ messageAccount);
messagesToAdd.add(m);
}
}
}
} else {
m.dirty = false; // as we reload the message, unset its dirty flag
- messageHelper.populate(m, message, new FolderInfoHolder(MessageList.this, messageFolder, account), account);
+ FolderInfoHolder folderInfoHolder = new FolderInfoHolder(MessageList.this,
+ 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,
+ 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) {
- addOrUpdateMessages(account, folder, messages, false);
+ public void listLocalMessagesAddMessages(Account account, String folder,
+ List messages) {
+ mHandler.addOrUpdateMessages(account, folder, messages, false);
}
});
}
@@ -2171,35 +1970,69 @@ public class MessageList
}
if (!messagesToAdd.isEmpty()) {
- mHandler.addMessages(messagesToAdd);
+ addMessages(messagesToAdd);
}
if (needsSort) {
- mHandler.sortMessages();
- mHandler.resetUnreadCount();
+ sortMessages();
+ resetUnreadCount();
}
}
- public MessageInfoHolder getMessage(Message message) {
- return getMessage(message.makeMessageReference());
- }
- // XXX TODO - make this not use a for loop
- public MessageInfoHolder getMessage(MessageReference messageReference) {
- synchronized (mAdapter.messages) {
- for (MessageInfoHolder holder : mAdapter.messages) {
- /*
- * 2010-06-21 - cketti
- * Added null pointer check. Not sure what's causing 'holder'
- * to be null. See log provided in issue 1749, comment #15.
- *
- * Please remove this comment once the cause was found and the
- * bug(?) fixed.
- */
- if ((holder != null) && holder.message.equalsReference(messageReference)) {
- return holder;
- }
+ /**
+ * 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 (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;
}
@@ -2224,13 +2057,13 @@ public class MessageList
public void onClick(View v) {
// Perform action on clicks
MessageInfoHolder message = (MessageInfoHolder) getItem((Integer)v.getTag());
- onToggleFlag(message);
+ onToggleFlag(Arrays.asList(new MessageInfoHolder[]{message}));
}
};
@Override
public int getCount() {
- return messages.size();
+ return mMessages.size();
}
@Override
@@ -2246,20 +2079,14 @@ public class MessageList
return -1;
}
- public Object getItem(long position) {
- return getItem((int)position);
- }
-
@Override
public Object getItem(int position) {
try {
- synchronized (mAdapter.messages) {
- if (position < mAdapter.messages.size()) {
- return mAdapter.messages.get(position);
- }
+ if (position < mMessages.size()) {
+ return mMessages.get(position);
}
} catch (Exception e) {
- Log.e(K9.LOG_TAG, "getItem(" + position + "), but folder.messages.size() = " + mAdapter.messages.size(), e);
+ Log.e(K9.LOG_TAG, "getItem(" + position + "), but folder.messages.size() = " + mMessages.size(), e);
}
return null;
}
@@ -2272,13 +2099,8 @@ public class MessageList
if ((convertView != null) && (convertView.getId() == R.layout.message_list_item)) {
view = convertView;
} else {
- if (mTouchView) {
- view = mInflater.inflate(R.layout.message_list_item_touchable, parent, false);
- view.setId(R.layout.message_list_item);
- } else {
- view = mInflater.inflate(R.layout.message_list_item, parent, false);
- view.setId(R.layout.message_list_item);
- }
+ view = mInflater.inflate(R.layout.message_list_item, parent, false);
+ view.setId(R.layout.message_list_item);
}
MessageViewHolder holder = (MessageViewHolder) view.getTag();
@@ -2309,13 +2131,9 @@ public class MessageList
holder.subject.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListSubject());
holder.date.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListDate());
- if (mTouchView) {
- holder.preview.setLines(mPreviewLines);
- holder.preview.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListPreview());
+ holder.preview.setLines(mPreviewLines);
+ holder.preview.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListPreview());
- } else {
- holder.from.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListSender());
- }
view.setTag(holder);
}
@@ -2397,20 +2215,16 @@ public class MessageList
- holder.chip.setBackgroundDrawable(message.message.getFolder().getAccount().generateColorChip().drawable());
-/* UI stuff is all changing
-<<<<<<< HEAD
- holder.chip.getBackground().setAlpha(message.read ? 127 : 255);
- view.getBackground().setAlpha(message.downloaded ? 0 : 127);
- // ASH funky bug is here?
- if (message.uid.startsWith(K9.LOCAL_UID_PREFIX)) {
- view.setBackgroundColor(message.message.getFolder().getAccount().getChipColor());
- view.getBackground().setAlpha(31);
+ holder.chip.setBackgroundDrawable(message.message.getFolder().getAccount().generateColorChip(message.read).drawable());
+ // TODO: Make these colors part of the theme
+ if (K9.getK9Theme() == K9.THEME_LIGHT) {
+ // Light theme: light grey background for read messages
+ view.setBackgroundColor(message.read ?
+ Color.rgb(229, 229, 229) : Color.rgb(255, 255, 255));
+ } else {
+ // Dark theme: dark grey background for unread messages
+ view.setBackgroundColor(message.read ? 0 : Color.rgb(45, 45, 45));
}
-=======
-*/
- view.getBackground().setAlpha(message.read ? 100 : 0);
-//>>>>>>> 4.301
if ((message.message.getSubject() == null) || message.message.getSubject().equals("")) {
holder.subject.setText(getText(R.string.general_no_subject));
@@ -2421,8 +2235,6 @@ public class MessageList
int senderTypeface = message.read ? Typeface.NORMAL : Typeface.BOLD;
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.
@@ -2443,8 +2255,13 @@ public class MessageList
message.sender.length() + 1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ // TODO: Make these colors part of the theme
+ 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.rgb(128, 128, 128)), // How do I can specify the android.R.attr.textColorTertiary
+ str.setSpan(new ForegroundColorSpan(color), // How do I can specify the android.R.attr.textColorTertiary
message.sender.length() + 1,
str.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
@@ -2481,21 +2298,10 @@ public class MessageList
}
}
-
-
-
@Override
public boolean hasStableIds() {
return true;
}
-
- public boolean isItemSelectable(int position) {
- if (position < mAdapter.messages.size()) {
- return true;
- } else {
- return false;
- }
- }
}
class MessageViewHolder
@@ -2531,7 +2337,6 @@ public class MessageList
selected.setVisibility(View.GONE);
}
}
- toggleBatchButtons();
}
}
}
@@ -2576,188 +2381,44 @@ public class MessageList
}
}
- private void hideBatchButtons() {
- if (mBatchButtonArea.getVisibility() != View.GONE) {
- mBatchButtonArea.setVisibility(View.GONE);
- mBatchButtonArea.startAnimation(
- AnimationUtils.loadAnimation(this, R.anim.footer_disappear));
- }
- }
-
- private void showBatchButtons() {
- if (mBatchButtonArea.getVisibility() != View.VISIBLE) {
- mBatchButtonArea.setVisibility(View.VISIBLE);
- Animation animation = AnimationUtils.loadAnimation(this, R.anim.footer_appear);
- animation.setAnimationListener(this);
- mBatchButtonArea.startAnimation(animation);
- }
- }
-
- private void toggleBatchButtons() {
-
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
-
- if (mSelectedCount < 0) {
- mSelectedCount = 0;
- }
-
- int readButtonIconId;
- int flagButtonIconId;
-
- if (mSelectedCount == 0) {
- readButtonIconId = R.drawable.ic_button_mark_read;
- flagButtonIconId = R.drawable.ic_button_flag;
- hideBatchButtons();
- } else {
- boolean newReadState = computeBatchDirection(false);
- if (newReadState) {
- readButtonIconId = R.drawable.ic_button_mark_read;
- } else {
- readButtonIconId = R.drawable.ic_button_mark_unread;
- }
- boolean newFlagState = computeBatchDirection(true);
- if (newFlagState) {
- flagButtonIconId = R.drawable.ic_button_flag;
- } else {
- flagButtonIconId = R.drawable.ic_button_unflag;
- }
- showBatchButtons();
- }
-
- mBatchReadButton.setImageResource(readButtonIconId);
- mBatchFlagButton.setImageResource(flagButtonIconId);
-
-
- }
- });
- }
-
static class FooterViewHolder {
public ProgressBar progress;
public TextView main;
}
-
- private boolean computeBatchDirection(boolean flagged) {
- boolean newState = false;
-
- synchronized (mAdapter.messages) {
- for (MessageInfoHolder holder : mAdapter.messages) {
- if (holder.selected) {
- if (flagged) {
- if (!holder.flagged) {
- newState = true;
- break;
- }
- } else {
- if (!holder.read) {
- newState = true;
- break;
- }
- }
- }
- }
- }
- return newState;
- }
-
- private boolean anySelected() {
- synchronized (mAdapter.messages) {
- for (MessageInfoHolder holder : mAdapter.messages) {
- if (holder.selected) {
- return true;
- }
- }
- }
- return false;
- }
-
- @Override
- public void onClick(View v) {
- boolean newState = false;
- List messageList = new ArrayList();
- List removeHolderList = new ArrayList();
-
- if (v == mBatchDoneButton) {
- setAllSelected(false);
- return;
- }
-
- if (v == mBatchFlagButton) {
- newState = computeBatchDirection(true);
- } else {
- newState = computeBatchDirection(false);
- }
-
- if (v == mBatchArchiveButton) {
- final List selection = getSelectionFromCheckboxes();
- onArchive(selection);
- return;
- }
-
- if (v == mBatchMoveButton) {
- final List selection = getSelectionFromCheckboxes();
- onMove(selection);
- return;
- }
-
- synchronized (mAdapter.messages) {
- for (MessageInfoHolder holder : mAdapter.messages) {
- if (holder.selected) {
- if (v == mBatchDeleteButton) {
- removeHolderList.add(holder);
- } else if (v == mBatchFlagButton) {
- holder.flagged = newState;
- } else if (v == mBatchReadButton) {
- holder.read = newState;
- }
- messageList.add(holder.message);
- }
- }
- }
- mAdapter.removeMessages(removeHolderList);
-
- if (!messageList.isEmpty()) {
- if (v == mBatchDeleteButton) {
- mController.deleteMessages(messageList.toArray(EMPTY_MESSAGE_ARRAY), null);
- mSelectedCount = 0;
- toggleBatchButtons();
- } else {
- mController.setFlag(messageList.toArray(EMPTY_MESSAGE_ARRAY), (v == mBatchReadButton ? Flag.SEEN : Flag.FLAGGED), newState);
- }
- } else {
- // Should not happen
- Toast.makeText(this, R.string.no_message_seletected_toast, Toast.LENGTH_SHORT).show();
- }
- mHandler.sortMessages();
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- }
-
- @Override
- public void onAnimationStart(Animation animation) {
- }
-
-
-
private void setAllSelected(boolean isSelected) {
mSelectedCount = 0;
- synchronized (mAdapter.messages) {
- for (MessageInfoHolder holder : mAdapter.messages) {
- holder.selected = isSelected;
- mSelectedCount += (isSelected ? 1 : 0);
- }
+
+ for (MessageInfoHolder holder : mAdapter.getMessages()) {
+ holder.selected = isSelected;
+ mSelectedCount += (isSelected ? 1 : 0);
}
+
mAdapter.notifyDataSetChanged();
- toggleBatchButtons();
+ }
+
+ /**
+ * Toggle all selected message states. Sort of. If anything selected, unselect everything. If nothing is
+ * selected, select everything.
+ */
+ private void toggleAllSelected() {
+ boolean newState = true;
+
+ // If there was anything selected, unselect everything.
+ if (mSelectedCount > 0) {
+ newState = false;
+ }
+
+ mAdapter.setSelectionForAllMesages(newState);
+
+ if (newState) {
+ mSelectedCount = mAdapter.getCount();
+ mActionMode = MessageList.this.startActionMode(mActionModeCallback);
+ mActionMode.setTitle(String.format(getString(R.string.actionbar_selected), mSelectedCount));
+ } else {
+ mSelectedCount = 0;
+ mActionMode.finish();
+ }
}
private void setSelected(final List holders, final boolean newState) {
@@ -2768,7 +2429,21 @@ public class MessageList
}
}
mAdapter.notifyDataSetChanged();
- toggleBatchButtons();
+ }
+
+ private void toggleMessageSelect(final MessageInfoHolder holder){
+ if (holder.selected) {
+ holder.selected = false;
+ mSelectedCount -= 1;
+ } else {
+ holder.selected = true;
+ mSelectedCount += 1;
+ }
+ mAdapter.notifyDataSetChanged();
+ mActionMode.setTitle(String.format(getString(R.string.actionbar_selected), mSelectedCount));
+
+ // make sure the onPrepareActionMode is called
+ mActionMode.invalidate();
}
/**
@@ -2796,7 +2471,7 @@ public class MessageList
}
}
mController.setFlag(messageList, flag, newState);
- mHandler.sortMessages();
+ mAdapter.sortMessages();
}
/**
@@ -2932,13 +2607,13 @@ public class MessageList
*/
private List getSelectionFromCheckboxes() {
final List selection = new ArrayList();
- synchronized (mAdapter.messages) {
- for (final MessageInfoHolder holder : mAdapter.messages) {
- if (holder.selected) {
- selection.add(holder);
- }
+
+ for (final MessageInfoHolder holder : mAdapter.getMessages()) {
+ if (holder.selected) {
+ selection.add(holder);
}
}
+
return selection;
}
@@ -2967,6 +2642,8 @@ public class MessageList
* {@link #move(List, String)}. This method was added mainly because those 2
* methods share common behavior.
*
+ * Note: Must be called from the UI thread!
+ *
* @param holders
* Never {@code null}.
* @param destination
@@ -3002,7 +2679,7 @@ public class MessageList
if (operation == FolderOperation.MOVE) {
mController.moveMessages(account, folderName, messages.toArray(new Message[messages.size()]), destination,
null);
- mHandler.removeMessages(holders);
+ mAdapter.removeMessages(holders);
} else {
mController.copyMessages(account, folderName, messages.toArray(new Message[messages.size()]), destination,
null);
@@ -3015,6 +2692,7 @@ public class MessageList
Accounts.listAccounts(this);
}
+
/**
* Return the currently "open" account if available.
*
@@ -3036,4 +2714,204 @@ public class MessageList
return account;
}
+
+ private boolean handleContextRelatedClick(int position){
+ MessageInfoHolder holder = (MessageInfoHolder) mAdapter.getItem(position);
+ if (mActionMode != null) {
+ if (mSelectedCount > 1) {
+ toggleMessageSelect(holder);
+ } else {
+ if (holder.selected) {
+ mActionMode.finish();
+ } else {
+ toggleMessageSelect(holder);
+ }
+ }
+ } else {
+ mActionMode = MessageList.this.startActionMode(mActionModeCallback);
+ toggleMessageSelect(holder);
+ }
+
+ return true;
+ }
+
+ private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+
+ // enable or disable forward, reply,....
+ menu.findItem(R.id.single_message_options)
+ .setVisible(mSelectedCount > 1 ? false : true);
+
+ if (mQueryString != null) {
+ // show all
+ menu.findItem(R.id.move).setVisible(true);
+ menu.findItem(R.id.archive).setVisible(true);
+ menu.findItem(R.id.spam).setVisible(true);
+ menu.findItem(R.id.copy).setVisible(true);
+
+ // hide uncapable
+ /*
+ * TODO think of a better way then looping over all
+ * messages.
+ */
+ final List selection = getSelectionFromCheckboxes();
+ Account account;
+
+ for (MessageInfoHolder holder : selection) {
+ account = holder.message.getFolder().getAccount();
+ setContextCapabilities(account, menu);
+ }
+
+ }
+ return true;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ mActionMode = null;
+ setAllSelected(false);
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ MenuInflater inflater = mode.getMenuInflater();
+ inflater.inflate(R.menu.message_list_context, menu);
+
+ // check capabilities
+ if (mQueryString == null) {
+ setContextCapabilities(mAccount, menu);
+ }
+
+ return true;
+ }
+
+ /**
+ * Disables menu options based on if the account supports it or not.
+ * It also checks the controller and for now the 'mode' the messagelist
+ * is operation in ( query or not ).
+ *
+ * @param mAccount Account to check capabilities of.
+ * @param menu Menu to adapt.
+ */
+ private void setContextCapabilities(Account mAccount, Menu menu) {
+ /*
+ * TODO get rid of this when we finally split the messagelist into
+ * a folder content display and a search result display
+ */
+ if (mQueryString != null) {
+ menu.findItem(R.id.move).setVisible(false);
+ menu.findItem(R.id.copy).setVisible(false);
+
+ menu.findItem(R.id.archive).setVisible(false);
+ menu.findItem(R.id.spam).setVisible(false);
+
+ return;
+ }
+
+ // hide unsupported
+ if (!mController.isCopyCapable(mAccount)) {
+ menu.findItem(R.id.copy).setVisible(false);
+ }
+
+ if (!mController.isMoveCapable(mAccount)) {
+ menu.findItem(R.id.move).setVisible(false);
+ menu.findItem(R.id.archive).setVisible(false);
+ menu.findItem(R.id.spam).setVisible(false);
+ }
+
+ if (!mAccount.hasArchiveFolder()) {
+ menu.findItem(R.id.archive).setVisible(false);
+ }
+
+ if (!mAccount.hasSpamFolder()) {
+ menu.findItem(R.id.spam).setVisible(false);
+ }
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ final List selection = getSelectionFromCheckboxes();
+
+ /*
+ * In the following we assume that we can't move or copy
+ * mails to the same folder. Also that spam isn't available if we are
+ * in the spam folder,same for archive.
+ *
+ * This is the case currently so safe assumption.
+ */
+ switch (item.getItemId()) {
+ case R.id.delete: {
+ onDelete(selection);
+ mSelectedCount = 0;
+ break;
+ }
+ case R.id.read_toggle: {
+ onToggleRead(selection);
+ break;
+ }
+ case R.id.flag_toggle: {
+ onToggleFlag(selection);
+ break;
+ }
+
+ // only if the account supports this
+ case R.id.archive: {
+ onArchive(selection);
+ mSelectedCount = 0;
+ break;
+ }
+ case R.id.spam: {
+ onSpam(selection);
+ mSelectedCount = 0;
+ break;
+ }
+ case R.id.move: {
+ onMove(selection);
+ mSelectedCount = 0;
+ break;
+ }
+ case R.id.copy: {
+ onCopy(selection);
+ mSelectedCount = 0;
+ break;
+ }
+
+ // only if a single message is selected
+ case R.id.reply: {
+ onReply(selection.get(0));
+ mSelectedCount = 0;
+ break;
+ }
+ case R.id.reply_all: {
+ onReplyAll(selection.get(0));
+ mSelectedCount = 0;
+ break;
+ }
+ case R.id.forward: {
+ onForward(selection.get(0));
+ mSelectedCount = 0;
+ break;
+ }
+ case R.id.send_again: {
+ onResendMessage(selection.get(0));
+ mSelectedCount = 0;
+ break;
+ }
+ case R.id.same_sender: {
+ MessageList.actionHandle(MessageList.this, "From " + selection.get(0).sender,
+ selection.get(0).senderAddress, false, null, null);
+ mSelectedCount = 0;
+ break;
+ }
+ }
+
+ if (mSelectedCount == 0) {
+ mActionMode.finish();
+ }
+
+ return true;
+ }
+ };
}
diff --git a/src/com/fsck/k9/activity/MessageView.java b/src/com/fsck/k9/activity/MessageView.java
index c4fb16189..2117158e1 100644
--- a/src/com/fsck/k9/activity/MessageView.java
+++ b/src/com/fsck/k9/activity/MessageView.java
@@ -1,5 +1,10 @@
package com.fsck.k9.activity;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
@@ -8,29 +13,40 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
-import android.view.*;
+import android.view.GestureDetector;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
import android.view.View.OnClickListener;
-import android.widget.*;
-import com.fsck.k9.*;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.Window;
+import com.fsck.k9.Account;
+import com.fsck.k9.K9;
+import com.fsck.k9.Preferences;
+import com.fsck.k9.R;
import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.controller.MessagingListener;
import com.fsck.k9.crypto.PgpData;
import com.fsck.k9.helper.FileBrowserHelper;
import com.fsck.k9.helper.FileBrowserHelper.FileBrowserFailOverCallback;
-import com.fsck.k9.mail.*;
+import com.fsck.k9.mail.Flag;
+import com.fsck.k9.mail.Message;
+import com.fsck.k9.mail.MessagingException;
+import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
import com.fsck.k9.mail.store.StorageManager;
import com.fsck.k9.view.AttachmentView;
-import com.fsck.k9.view.SingleMessageView;
import com.fsck.k9.view.AttachmentView.AttachmentFileDownloadCallback;
-
-import java.io.File;
-import java.util.*;
+import com.fsck.k9.view.MessageTitleView;
+import com.fsck.k9.view.SingleMessageView;
public class MessageView extends K9Activity implements OnClickListener {
private static final String EXTRA_MESSAGE_REFERENCE = "com.fsck.k9.MessageView_messageReference";
private static final String EXTRA_MESSAGE_REFERENCES = "com.fsck.k9.MessageView_messageReferences";
- private static final String EXTRA_NEXT = "com.fsck.k9.MessageView_next";
private static final String EXTRA_MESSAGE_LIST_EXTRAS = "com.fsck.k9.MessageView_messageListExtras";
private static final String STATE_PGP_DATA = "pgpData";
private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;
@@ -38,15 +54,10 @@ public class MessageView extends K9Activity implements OnClickListener {
private static final int ACTIVITY_CHOOSE_DIRECTORY = 3;
private SingleMessageView mMessageView;
+ private MessageTitleView mTitleView;
private PgpData mPgpData;
-
- private View mNext;
- private View mPrevious;
- private View mDelete;
- private View mArchive;
- private View mMove;
- private View mSpam;
+ private Menu mMenu;
private Account mAccount;
private MessageReference mMessageReference;
private ArrayList mMessageReferences;
@@ -234,24 +245,12 @@ public class MessageView extends K9Activity implements OnClickListener {
return super.onKeyUp(keyCode, event);
}
- @Override
- public void onBackPressed() {
- if (K9.manageBack()) {
- if (mMessageListExtras != null) {
- MessageList.actionHandleFolder(this, mMessageListExtras);
- }
- finish();
- } else {
- super.onBackPressed();
- }
- }
-
class MessageViewHandler extends Handler {
public void progress(final boolean progress) {
runOnUiThread(new Runnable() {
public void run() {
- setProgressBarIndeterminateVisibility(progress);
+ setSupportProgressBarIndeterminateVisibility(progress);
}
});
}
@@ -290,10 +289,10 @@ public class MessageView extends K9Activity implements OnClickListener {
public static void actionView(Context context, MessageReference messRef,
ArrayList messReferences, Bundle messageListExtras) {
Intent i = new Intent(context, MessageView.class);
+ i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
i.putExtra(EXTRA_MESSAGE_LIST_EXTRAS, messageListExtras);
i.putExtra(EXTRA_MESSAGE_REFERENCE, messRef);
i.putParcelableArrayListExtra(EXTRA_MESSAGE_REFERENCES, messReferences);
- i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
@@ -301,7 +300,6 @@ public class MessageView extends K9Activity implements OnClickListener {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.message_view);
mMessageView = (SingleMessageView) findViewById(R.id.message_view);
@@ -334,6 +332,8 @@ public class MessageView extends K9Activity implements OnClickListener {
});
mMessageView.initialize(this);
+ mMessageView.downloadRemainderButton().setOnClickListener(this);
+ initializeActionBar();
setTitle("");
final Intent intent = getIntent();
@@ -383,51 +383,14 @@ public class MessageView extends K9Activity implements OnClickListener {
}
mAccount = Preferences.getPreferences(this).getAccount(mMessageReference.accountUuid);
-
-
- if (K9.DEBUG)
- Log.d(K9.LOG_TAG, "MessageView got message " + mMessageReference);
- if (intent.getBooleanExtra(EXTRA_NEXT, false)) {
- mNext.requestFocus();
- }
-
mScreenWidthInPixels = getResources().getDisplayMetrics().widthPixels;
// Enable gesture detection for MessageViews
mGestureDetector = new GestureDetector(new MyGestureDetector(false));
- setupButtonViews();
displayMessage(mMessageReference);
}
- private void setupButtonViews() {
- setOnClickListener(R.id.from);
- setOnClickListener(R.id.reply);
- setOnClickListener(R.id.reply_all);
- setOnClickListener(R.id.delete);
- setOnClickListener(R.id.forward);
- setOnClickListener(R.id.next);
- setOnClickListener(R.id.previous);
- setOnClickListener(R.id.archive);
- setOnClickListener(R.id.move);
- setOnClickListener(R.id.spam);
- setOnClickListener(R.id.download_remainder);
-
-
- mNext = findViewById(R.id.next);
- mPrevious = findViewById(R.id.previous);
- mDelete = findViewById(R.id.delete);
-
- mArchive = findViewById(R.id.archive);
- mMove = findViewById(R.id.move);
- mSpam = findViewById(R.id.spam);
-
- if (!mAccount.getEnableMoveButtons()) {
- View buttons = findViewById(R.id.move_buttons);
- buttons.setVisibility(View.GONE);
- }
- }
-
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -443,6 +406,18 @@ public class MessageView extends K9Activity implements OnClickListener {
mMessageView.updateCryptoLayout(mAccount.getCryptoProvider(), mPgpData, mMessage);
}
+ private void initializeActionBar() {
+ final ActionBar actionBar = getSupportActionBar();
+
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowCustomEnabled(true);
+ actionBar.setCustomView(R.layout.actionbar_message_view);
+
+ final View customView = actionBar.getCustomView();
+ mTitleView = (MessageTitleView) customView.findViewById(android.R.id.title);
+ mTitleView.setMessageHeader(mMessageView.getMessageHeaderView());
+ }
+
private void displayMessage(MessageReference ref) {
mMessageReference = ref;
if (K9.DEBUG)
@@ -457,47 +432,7 @@ public class MessageView extends K9Activity implements OnClickListener {
mMessageView.resetHeaderView();
mController.loadMessageForView(mAccount, mMessageReference.folderName, mMessageReference.uid, mListener);
- setupDisplayMessageButtons();
- }
-
- private void setupDisplayMessageButtons() {
- mDelete.setEnabled(true);
- mNext.setEnabled(mNextMessage != null);
- mPrevious.setEnabled(mPreviousMessage != null);
- // If moving isn't support at all, then all of them must be disabled anyway.
- if (mController.isMoveCapable(mAccount)) {
- // Only enable the button if they have an archive folder and it's not the current folder.
- mArchive.setEnabled(!mMessageReference.folderName.equals(mAccount.getArchiveFolderName()) &&
- !K9.FOLDER_NONE.equalsIgnoreCase(mAccount.getArchiveFolderName()));
- // Only enable the button if the Spam folder is not the current folder and not NONE.
- mSpam.setEnabled(!mMessageReference.folderName.equals(mAccount.getSpamFolderName()) &&
- //ASH !K9.FOLDER_NONE.equalsIgnoreCase(mAccount.getSpamFolderName()));
- mAccount.hasSpamFolder());
- mMove.setEnabled(true);
- } else {
- disableMoveButtons();
- }
- }
-
- private void disableButtons() {
- mMessageView.setLoadPictures(false);
- disableMoveButtons();
- mNext.setEnabled(false);
- mPrevious.setEnabled(false);
- mDelete.setEnabled(false);
- }
-
- private void disableMoveButtons() {
- mArchive.setEnabled(false);
- mMove.setEnabled(false);
- mSpam.setEnabled(false);
- }
-
- private void setOnClickListener(int viewCode) {
- View thisView = findViewById(viewCode);
- if (thisView != null) {
- thisView.setOnClickListener(this);
- }
+ configureMenu(mMenu);
}
private void findSurroundingMessagesUid() {
@@ -548,7 +483,7 @@ public class MessageView extends K9Activity implements OnClickListener {
if (mMessage != null) {
// Disable the delete button after it's tapped (to try to prevent
// accidental clicks)
- disableButtons();
+ mMenu.findItem(R.id.delete).setEnabled(false);
Message messageToDelete = mMessage;
showNextMessageOrReturn();
mController.deleteMessages(new Message[] {messageToDelete}, null);
@@ -736,12 +671,11 @@ public class MessageView extends K9Activity implements OnClickListener {
return;
}
mLastDirection = NEXT;
- disableButtons();
+ //toggleActionsState(mMenu, false);
if (K9.showAnimations()) {
mMessageView.startAnimation(outToLeftAnimation());
}
displayMessage(mNextMessage);
- mNext.requestFocus();
}
protected void onPrevious() {
@@ -751,12 +685,11 @@ public class MessageView extends K9Activity implements OnClickListener {
return;
}
mLastDirection = PREVIOUS;
- disableButtons();
+ //toggleActionsState(mMenu, false);
if (K9.showAnimations()) {
mMessageView.startAnimation(inFromRightAnimation());
}
displayMessage(mPreviousMessage);
- mPrevious.requestFocus();
}
private void onToggleRead() {
@@ -766,6 +699,7 @@ public class MessageView extends K9Activity implements OnClickListener {
mMessageView.setHeaders(mMessage, mAccount);
String subject = mMessage.getSubject();
setTitle(subject);
+ updateUnreadToggleTitle();
}
}
@@ -780,33 +714,6 @@ public class MessageView extends K9Activity implements OnClickListener {
public void onClick(View view) {
switch (view.getId()) {
- case R.id.reply:
- onReply();
- break;
- case R.id.reply_all:
- onReplyAll();
- break;
- case R.id.delete:
- onDelete();
- break;
- case R.id.forward:
- onForward();
- break;
- case R.id.archive:
- onRefile(mAccount.getArchiveFolderName());
- break;
- case R.id.spam:
- onRefile(mAccount.getSpamFolderName());
- break;
- case R.id.move:
- onMove();
- break;
- case R.id.next:
- onNext();
- break;
- case R.id.previous:
- onPrevious();
- break;
case R.id.download:
((AttachmentView)view).saveFile();
break;
@@ -819,6 +726,15 @@ public class MessageView extends K9Activity implements OnClickListener {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ break;
+ case R.id.next_message:
+ onNext();
+ break;
+ case R.id.previous_message:
+ onPrevious();
+ break;
case R.id.delete:
onDelete();
break;
@@ -831,15 +747,12 @@ public class MessageView extends K9Activity implements OnClickListener {
case R.id.forward:
onForward();
break;
- case R.id.send_alternate:
+ case R.id.share:
onSendAlternate();
break;
- case R.id.mark_as_unread:
+ case R.id.toggle_unread:
onToggleRead();
break;
- case R.id.flag:
- onFlag();
- break;
case R.id.archive:
onRefile(mAccount.getArchiveFolderName());
break;
@@ -855,14 +768,6 @@ public class MessageView extends K9Activity implements OnClickListener {
case R.id.upload:
onUpload();
break;
- case R.id.show_full_header:
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mMessageView.showAllHeaders();
- }
- });
- break;
case R.id.select_text:
mMessageView.beginSelectingText();
break;
@@ -875,21 +780,96 @@ public class MessageView extends K9Activity implements OnClickListener {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.message_view_option, menu);
- if (!mController.isCopyCapable(mAccount)) {
- menu.findItem(R.id.copy).setVisible(false);
+ getSupportMenuInflater().inflate(R.menu.message_view_option, menu);
+ mMenu = menu;
+ configureMenu(menu);
+ return true;
+ }
+
+ private void configureMenu(Menu menu) {
+ // first run displayMessage() gets called before onCreateOptionMenu()
+ if (menu == null) {
+ return;
}
- if (!mController.isMoveCapable(mAccount)) {
+
+ // enable them all
+ menu.findItem(R.id.copy).setVisible(true);
+ menu.findItem(R.id.move).setVisible(true);
+ menu.findItem(R.id.archive).setVisible(true);
+ menu.findItem(R.id.spam).setVisible(true);
+ toggleActionsState(menu, true);
+
+ if (mNextMessage != null) {
+
+ menu.findItem(R.id.next_message).setEnabled(true);
+ menu.findItem(R.id.next_message).getIcon().setAlpha(255);
+ } else {
+ menu.findItem(R.id.next_message).getIcon().setAlpha(127);
+ menu.findItem(R.id.next_message).setEnabled(false);
+ }
+
+ if (mPreviousMessage != null) {
+ menu.findItem(R.id.previous_message).setEnabled(true);
+ menu.findItem(R.id.previous_message).getIcon().setAlpha(255);
+ } else {
+ menu.findItem(R.id.previous_message).getIcon().setAlpha(127);
+ menu.findItem(R.id.previous_message).setEnabled(false);
+ }
+
+ updateUnreadToggleTitle();
+
+ // comply with the setting
+ if (!mAccount.getEnableMoveButtons()) {
menu.findItem(R.id.move).setVisible(false);
menu.findItem(R.id.archive).setVisible(false);
menu.findItem(R.id.spam).setVisible(false);
+ } else {
+ // check message, folder capability
+ if (!mController.isCopyCapable(mAccount)) {
+ menu.findItem(R.id.copy).setVisible(false);
+ }
+
+ if (mController.isMoveCapable(mAccount)) {
+ menu.findItem(R.id.move).setVisible(true);
+
+ menu.findItem(R.id.archive).setVisible(
+ !mMessageReference.folderName.equals(mAccount.getArchiveFolderName())
+ && mAccount.hasArchiveFolder());
+
+ menu.findItem(R.id.spam).setVisible(
+ !mMessageReference.folderName.equals(mAccount.getSpamFolderName())
+ && mAccount.hasSpamFolder());
+ } else {
+ menu.findItem(R.id.copy).setVisible(false);
+ menu.findItem(R.id.move).setVisible(false);
+ menu.findItem(R.id.archive).setVisible(false);
+ menu.findItem(R.id.spam).setVisible(false);
+ }
}
+ // ASH probably should not be here
if (K9.FOLDER_NONE.equalsIgnoreCase(mAccount.getArchiveFolderName())) {
menu.findItem(R.id.archive).setVisible(false);
}
- if (!mAccount.hasSpamFolder()) {
- menu.findItem(R.id.spam).setVisible(false);
+ }
+
+ /**
+ * Set the title of the "Toggle Unread" menu item based upon the current read state of the message.
+ */
+ public void updateUnreadToggleTitle() {
+ if (mMessage != null && mMenu != null) {
+ if (mMessage.isSet(Flag.SEEN)) {
+ mMenu.findItem(R.id.toggle_unread).setTitle(R.string.mark_as_unread_action);
+ } else {
+ mMenu.findItem(R.id.toggle_unread).setTitle(R.string.mark_as_read_action);
+ }
}
+ }
+
+ private void toggleActionsState(Menu menu, boolean state) {
+ for (int i = 0; i < menu.size(); ++i) {
+ menu.getItem(i).setEnabled(state);
+ }
+ // ASH ↓↓↓
try {
if (!mMessage.getUid().startsWith(K9.LOCAL_UID_PREFIX) ||
((com.fsck.k9.mail.store.LocalStore.LocalFolder)mMessage.getFolder()).isLocalOnly() ||
@@ -899,7 +879,8 @@ public class MessageView extends K9Activity implements OnClickListener {
} catch (com.fsck.k9.mail.MessagingException e) {
Log.e(K9.LOG_TAG, "Error trying to get remote store: " + e);
}
- return true;
+ // WTF return true;
+ // ASH ↑↑↑
}
// TODO: when switching to API version 8, override onCreateDialog(int, Bundle)
@@ -946,28 +927,6 @@ public class MessageView extends K9Activity implements OnClickListener {
return super.onCreateDialog(id);
}
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- if (menu != null) {
- MenuItem flagItem = menu.findItem(R.id.flag);
- if (flagItem != null && mMessage != null) {
- flagItem.setTitle((mMessage.isSet(Flag.FLAGGED) ? R.string.unflag_action : R.string.flag_action));
- }
- MenuItem additionalHeadersItem = menu.findItem(R.id.show_full_header);
- if (additionalHeadersItem != null) {
- additionalHeadersItem.setTitle(mMessageView.additionalHeadersVisible() ?
- R.string.hide_full_header_action : R.string.show_full_header_action);
- }
-
- if (mMessage != null) {
- int actionTitle = mMessage.isSet(Flag.SEEN) ?
- R.string.mark_as_unread_action : R.string.mark_as_read_action;
- menu.findItem(R.id.mark_as_unread).setTitle(actionTitle);
- }
- }
- return super.onPrepareOptionsMenu(menu);
- }
-
class Listener extends MessagingListener {
@Override
public void loadMessageForViewHeadersAvailable(final Account account, String folder, String uid,
@@ -999,6 +958,12 @@ public class MessageView extends K9Activity implements OnClickListener {
mMessageView.showStatusMessage(text);
}
mMessageView.setHeaders(clonedMessage, account);
+ final String subject = clonedMessage.getSubject();
+ if (subject == null || subject.equals("")) {
+ setTitle(getString(R.string.general_no_subject));
+ } else {
+ setTitle(clonedMessage.getSubject());
+ }
mMessageView.setOnFlagListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -1025,6 +990,7 @@ public class MessageView extends K9Activity implements OnClickListener {
mMessage = message;
mMessageView.setMessage(account, (LocalMessage) message, mPgpData,
mController, mListener);
+ updateUnreadToggleTitle();
} catch (MessagingException e) {
Log.v(K9.LOG_TAG, "loadMessageForViewBodyAvailable", e);
@@ -1041,7 +1007,7 @@ public class MessageView extends K9Activity implements OnClickListener {
}
mHandler.post(new Runnable() {
public void run() {
- setProgressBarIndeterminateVisibility(false);
+ setSupportProgressBarIndeterminateVisibility(false);
if (t instanceof IllegalArgumentException) {
mHandler.invalidIdError();
} else {
@@ -1063,7 +1029,7 @@ public class MessageView extends K9Activity implements OnClickListener {
}
mHandler.post(new Runnable() {
public void run() {
- setProgressBarIndeterminateVisibility(false);
+ setSupportProgressBarIndeterminateVisibility(false);
mMessageView.setShowDownloadButton(message);
}
});
@@ -1077,7 +1043,7 @@ public class MessageView extends K9Activity implements OnClickListener {
}
mHandler.post(new Runnable() {
public void run() {
- setProgressBarIndeterminateVisibility(true);
+ setSupportProgressBarIndeterminateVisibility(true);
}
});
}
@@ -1146,4 +1112,14 @@ public class MessageView extends K9Activity implements OnClickListener {
Log.e(K9.LOG_TAG, "displayMessageBody failed", e);
}
}
+
+ /**
+ * Set the title of the view. Since we're using a custom ActionBar view, the normal setTitle() doesn't do what we
+ * think. This version sets the text value into the proper ActionBar title view.
+ * @param title Title to set.
+ */
+ @Override
+ public void setTitle(CharSequence title) {
+ mTitleView.setText(title);
+ }
}
diff --git a/src/com/fsck/k9/activity/misc/ActionBarNavigationSpinner.java b/src/com/fsck/k9/activity/misc/ActionBarNavigationSpinner.java
new file mode 100644
index 000000000..912ee07d7
--- /dev/null
+++ b/src/com/fsck/k9/activity/misc/ActionBarNavigationSpinner.java
@@ -0,0 +1,90 @@
+package com.fsck.k9.activity.misc;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+
+import com.fsck.k9.R;
+
+
+public class ActionBarNavigationSpinner extends ArrayAdapter implements SpinnerAdapter {
+
+ public static final long AB_NAVIGATION_INBOX = 0L;
+ public static final long AB_NAVIGATION_FOLDERS = 1L;
+ public static final long AB_NAVIGATION_ACCOUNTS = 2L;
+
+ private String mTitle = "";
+ private String mSubTitle = "";
+ private long[] mIds;
+ private Context mContext;
+
+ public ActionBarNavigationSpinner(Context context, String[] objects, long[] ids) {
+ super(context, R.layout.actionbar_spinner, objects);
+ setDropDownViewResource(android.R.layout.simple_list_item_1);
+ mIds = ids;
+ this.mContext = context;
+ }
+
+ public boolean setTitle(String title) {
+ if (!title.equals(mTitle)) {
+ mTitle = title;
+ notifyDataSetChanged();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean setSubTitle(String subtitle) {
+ if (!subtitle.equals(mSubTitle)) {
+ mSubTitle = subtitle;
+ notifyDataSetChanged();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ // TODO Auto-generated method stub
+ return super.getDropDownView(position, convertView, parent);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View row = convertView;
+
+ if (row == null) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ row = inflater.inflate(R.layout.actionbar_spinner, parent, false);
+ }
+
+ TextView title = (TextView) row.findViewById(R.id.actionbar_title_first);
+ TextView subtitle = (TextView) row.findViewById(R.id.actionbar_title_sub);
+
+ title.setText(mTitle);
+ subtitle.setText(mSubTitle);
+
+ return row;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mIds[position];
+ }
+
+ public static ActionBarNavigationSpinner getDefaultSpinner(Context context) {
+ return new ActionBarNavigationSpinner(context,
+ new String[] {
+ context.getString(R.string.special_mailbox_name_inbox),
+ context.getString(R.string.folders_title),
+ context.getString(R.string.accounts_title) },
+ new long[] {
+ ActionBarNavigationSpinner.AB_NAVIGATION_INBOX,
+ ActionBarNavigationSpinner.AB_NAVIGATION_FOLDERS,
+ ActionBarNavigationSpinner.AB_NAVIGATION_ACCOUNTS });
+ }
+}
diff --git a/src/com/fsck/k9/activity/misc/SwipeGestureDetector.java b/src/com/fsck/k9/activity/misc/SwipeGestureDetector.java
new file mode 100644
index 000000000..f020e41fc
--- /dev/null
+++ b/src/com/fsck/k9/activity/misc/SwipeGestureDetector.java
@@ -0,0 +1,87 @@
+package com.fsck.k9.activity.misc;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.GestureDetector.SimpleOnGestureListener;
+
+
+public class SwipeGestureDetector extends SimpleOnGestureListener {
+ private static final float SWIPE_MAX_OFF_PATH_DIP = 250f;
+ private static final float SWIPE_THRESHOLD_VELOCITY_DIP = 325f;
+
+ private final OnSwipeGestureListener mListener;
+ private int mMinVelocity;
+ private int mMaxOffPath;
+ private MotionEvent mLastOnDownEvent = null;
+
+ public SwipeGestureDetector(Context context, OnSwipeGestureListener listener) {
+ super();
+
+ if (listener == null) {
+ throw new IllegalArgumentException("'listener' may not be null");
+ }
+
+ mListener = listener;
+
+ // Calculate the minimum distance required for this to count as a swipe.
+ // Convert the constant dips to pixels.
+ float gestureScale = context.getResources().getDisplayMetrics().density;
+ mMinVelocity = (int) (SWIPE_THRESHOLD_VELOCITY_DIP * gestureScale + 0.5f);
+ mMaxOffPath = (int) (SWIPE_MAX_OFF_PATH_DIP * gestureScale + 0.5f);
+ }
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ mLastOnDownEvent = e;
+ return super.onDown(e);
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+ // Apparently sometimes e1 is null
+ // Found a workaround here: http://stackoverflow.com/questions/4151385/
+ if (e1 == null) {
+ e1 = mLastOnDownEvent;
+ }
+
+ // Make sure we avoid NullPointerExceptions
+ if (e1 == null || e2 == null) {
+ return false;
+ }
+
+ // Calculate how much was actually swiped.
+ final float deltaX = e2.getX() - e1.getX();
+ final float deltaY = e2.getY() - e1.getY();
+
+ // Calculate the minimum distance required for this to be considered a swipe.
+ final int minDistance = (int) Math.abs(deltaY * 4);
+
+ try {
+ if (Math.abs(deltaY) > mMaxOffPath || Math.abs(velocityX) < mMinVelocity) {
+ return false;
+ }
+
+ if (deltaX < (minDistance * -1)) {
+ mListener.onSwipeRightToLeft(e1, e2);
+ } else if (deltaX > minDistance) {
+ mListener.onSwipeLeftToRight(e1, e2);
+ } else {
+ return false;
+ }
+
+ // successful fling, cancel the 2nd event to prevent any other action from happening
+ // see http://code.google.com/p/android/issues/detail?id=8497
+ e2.setAction(MotionEvent.ACTION_CANCEL);
+ } catch (Exception e) {
+ // nothing
+ }
+
+ return false;
+ }
+
+
+ public interface OnSwipeGestureListener {
+ void onSwipeRightToLeft(final MotionEvent e1, final MotionEvent e2);
+ void onSwipeLeftToRight(final MotionEvent e1, final MotionEvent e2);
+ }
+}
diff --git a/src/com/fsck/k9/activity/setup/AccountSettings.java b/src/com/fsck/k9/activity/setup/AccountSettings.java
index 904459891..98d8d3893 100644
--- a/src/com/fsck/k9/activity/setup/AccountSettings.java
+++ b/src/com/fsck/k9/activity/setup/AccountSettings.java
@@ -158,7 +158,6 @@ public class AccountSettings extends K9PreferenceActivity {
private CheckBoxPreference mReplyAfterQuote;
private CheckBoxPreference mStripSignature;
private CheckBoxPreference mSyncRemoteDeletions;
- private CheckBoxPreference mSaveAllHeaders;
private CheckBoxPreference mAutoUploadOnMove;
private CheckBoxPreference mPushPollOnConnect;
private ListPreference mIdleRefreshPeriod;
@@ -369,9 +368,6 @@ public class AccountSettings extends K9PreferenceActivity {
mSyncRemoteDeletions = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_REMOTE_DELETIONS);
mSyncRemoteDeletions.setChecked(mAccount.syncRemoteDeletions());
- mSaveAllHeaders = (CheckBoxPreference) findPreference(PREFERENCE_SAVE_ALL_HEADERS);
- mSaveAllHeaders.setChecked(mAccount.saveAllHeaders());
-
mAutoUploadOnMove = (CheckBoxPreference) findPreference(PREFERENCE_AUTO_UPLOAD_ON_MOVE);
mAutoUploadOnMove.setChecked(mAccount.isAutoUploadOnMove() && mIsAppendCapable);
if (!mIsAppendCapable || !K9.isShowAdvancedOptions()) {
@@ -731,7 +727,6 @@ Log.d("ASH", "Have set delete policy to " + mAccount.getDeletePolicy());
mAccount.setExpungePolicy(mExpungePolicy.getValue());
}
mAccount.setSyncRemoteDeletions(mSyncRemoteDeletions.isChecked());
- mAccount.setSaveAllHeaders(mSaveAllHeaders.isChecked());
mAccount.setAutoUploadOnMove(mAutoUploadOnMove.isChecked());
mAccount.setSearchableFolders(Account.Searchable.valueOf(mSearchableFolders.getValue()));
mAccount.setMessageFormat(Account.MessageFormat.valueOf(mMessageFormat.getValue()));
diff --git a/src/com/fsck/k9/activity/setup/AccountSetupNames.java b/src/com/fsck/k9/activity/setup/AccountSetupNames.java
index 6079f3825..61702c52f 100644
--- a/src/com/fsck/k9/activity/setup/AccountSetupNames.java
+++ b/src/com/fsck/k9/activity/setup/AccountSetupNames.java
@@ -13,6 +13,7 @@ import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import com.fsck.k9.*;
+import com.fsck.k9.activity.Accounts;
import com.fsck.k9.activity.K9Activity;
import com.fsck.k9.helper.Utility;
@@ -85,6 +86,7 @@ public class AccountSetupNames extends K9Activity implements OnClickListener {
}
mAccount.setName(mName.getText().toString());
mAccount.save(Preferences.getPreferences(this));
+ Accounts.listAccounts(this);
finish();
}
diff --git a/src/com/fsck/k9/activity/setup/FolderSettings.java b/src/com/fsck/k9/activity/setup/FolderSettings.java
index da74cdc6c..61b8ba42c 100644
--- a/src/com/fsck/k9/activity/setup/FolderSettings.java
+++ b/src/com/fsck/k9/activity/setup/FolderSettings.java
@@ -81,10 +81,9 @@ public class FolderSettings extends K9PreferenceActivity {
addPreferencesFromResource(R.xml.folder_settings_preferences);
- //ASH Preference category = findPreference(PREFERENCE_TOP_CATERGORY);
+ String displayName = FolderInfoHolder.getDisplayName(this, mAccount, mFolder.getName());
PreferenceCategory category = (PreferenceCategory)findPreference(PREFERENCE_TOP_CATERGORY);
- String displayName = FolderInfoHolder.getDisplayName(this, mFolder);
- category.setTitle(displayName); // ASH folderName
+ category.setTitle(displayName);
mInTopGroup = (CheckBoxPreference)findPreference(PREFERENCE_IN_TOP_GROUP);
diff --git a/src/com/fsck/k9/activity/setup/FontSizeSettings.java b/src/com/fsck/k9/activity/setup/FontSizeSettings.java
index f0d1cda18..81d3e0260 100644
--- a/src/com/fsck/k9/activity/setup/FontSizeSettings.java
+++ b/src/com/fsck/k9/activity/setup/FontSizeSettings.java
@@ -1,4 +1,3 @@
-
package com.fsck.k9.activity.setup;
import android.content.Context;
diff --git a/src/com/fsck/k9/activity/setup/Prefs.java b/src/com/fsck/k9/activity/setup/Prefs.java
index 726e15cde..71138552c 100644
--- a/src/com/fsck/k9/activity/setup/Prefs.java
+++ b/src/com/fsck/k9/activity/setup/Prefs.java
@@ -24,7 +24,6 @@ import com.fsck.k9.K9;
import com.fsck.k9.K9.NotificationHideSubject;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
-import com.fsck.k9.activity.Accounts;
import com.fsck.k9.activity.ColorPickerDialog;
import com.fsck.k9.activity.K9PreferenceActivity;
import com.fsck.k9.helper.DateFormatter;
@@ -54,14 +53,12 @@ public class Prefs extends K9PreferenceActivity {
private static final String PREFERENCE_ANIMATIONS = "animations";
private static final String PREFERENCE_GESTURES = "gestures";
private static final String PREFERENCE_VOLUME_NAVIGATION = "volumeNavigation";
- private static final String PREFERENCE_MANAGE_BACK = "manage_back";
private static final String PREFERENCE_START_INTEGRATED_INBOX = "start_integrated_inbox";
private static final String PREFERENCE_CONFIRM_ACTIONS = "confirm_actions";
private static final String PREFERENCE_NOTIFICATION_HIDE_SUBJECT = "notification_hide_subject";
private static final String PREFERENCE_MEASURE_ACCOUNTS = "measure_accounts";
private static final String PREFERENCE_COUNT_SEARCH = "count_search";
private static final String PREFERENCE_HIDE_SPECIAL_ACCOUNTS = "hide_special_accounts";
- private static final String PREFERENCE_MESSAGELIST_TOUCHABLE = "messagelist_touchable";
private static final String PREFERENCE_MESSAGELIST_PREVIEW_LINES = "messagelist_preview_lines";
private static final String PREFERENCE_MESSAGELIST_STARS = "messagelist_stars";
private static final String PREFERENCE_MESSAGELIST_CHECKBOXES = "messagelist_checkboxes";
@@ -99,14 +96,12 @@ public class Prefs extends K9PreferenceActivity {
private CheckBoxPreference mAnimations;
private CheckBoxPreference mGestures;
private CheckBoxListPreference mVolumeNavigation;
- private CheckBoxPreference mManageBack;
private CheckBoxPreference mStartIntegratedInbox;
private CheckBoxListPreference mConfirmActions;
private ListPreference mNotificationHideSubject;
private CheckBoxPreference mMeasureAccounts;
private CheckBoxPreference mCountSearch;
private CheckBoxPreference mHideSpecialAccounts;
- private CheckBoxPreference mTouchable;
private ListPreference mPreviewLines;
private CheckBoxPreference mStars;
private CheckBoxPreference mCheckboxes;
@@ -200,9 +195,6 @@ public class Prefs extends K9PreferenceActivity {
mVolumeNavigation.setItems(new CharSequence[] {getString(R.string.volume_navigation_message), getString(R.string.volume_navigation_list)});
mVolumeNavigation.setCheckedItems(new boolean[] {K9.useVolumeKeysForNavigationEnabled(), K9.useVolumeKeysForListNavigationEnabled()});
- mManageBack = (CheckBoxPreference)findPreference(PREFERENCE_MANAGE_BACK);
- mManageBack.setChecked(K9.manageBack());
-
mStartIntegratedInbox = (CheckBoxPreference)findPreference(PREFERENCE_START_INTEGRATED_INBOX);
mStartIntegratedInbox.setChecked(K9.startIntegratedInbox());
@@ -232,8 +224,6 @@ public class Prefs extends K9PreferenceActivity {
mHideSpecialAccounts = (CheckBoxPreference)findPreference(PREFERENCE_HIDE_SPECIAL_ACCOUNTS);
mHideSpecialAccounts.setChecked(K9.isHideSpecialAccounts());
- mTouchable = (CheckBoxPreference)findPreference(PREFERENCE_MESSAGELIST_TOUCHABLE);
- mTouchable.setChecked(K9.messageListTouchable());
mPreviewLines = setupListPreference(PREFERENCE_MESSAGELIST_PREVIEW_LINES,
Integer.toString(K9.messageListPreviewLines()));
@@ -422,7 +412,6 @@ public class Prefs extends K9PreferenceActivity {
K9.setShowAdvancedOptions(showAdvancedOptions.isChecked());
K9.setUseVolumeKeysForNavigation(mVolumeNavigation.getCheckedItems()[0]);
K9.setUseVolumeKeysForListNavigation(mVolumeNavigation.getCheckedItems()[1]);
- K9.setManageBack(mManageBack.isChecked());
K9.setStartIntegratedInbox(!mHideSpecialAccounts.isChecked() && mStartIntegratedInbox.isChecked());
K9.setConfirmDelete(mConfirmActions.getCheckedItems()[0]);
K9.setConfirmDeleteStarred(mConfirmActions.getCheckedItems()[1]);
@@ -433,7 +422,6 @@ public class Prefs extends K9PreferenceActivity {
K9.setMeasureAccounts(mMeasureAccounts.isChecked());
K9.setCountSearchMessages(mCountSearch.isChecked());
K9.setHideSpecialAccounts(mHideSpecialAccounts.isChecked());
- K9.setMessageListTouchable(mTouchable.isChecked());
K9.setMessageListPreviewLines(Integer.parseInt(mPreviewLines.getValue()));
K9.setMessageListStars(mStars.isChecked());
K9.setMessageListCheckboxes(mCheckboxes.isChecked());
@@ -482,16 +470,6 @@ public class Prefs extends K9PreferenceActivity {
super.onPause();
}
- @Override
- public void onBackPressed() {
- if (K9.manageBack()) {
- Accounts.listAccounts(this);
- finish();
- } else {
- super.onBackPressed();
- }
- }
-
private void onFontSizeSettings() {
FontSizeSettings.actionEditSettings(this);
}
diff --git a/src/com/fsck/k9/activity/setup/WelcomeMessage.java b/src/com/fsck/k9/activity/setup/WelcomeMessage.java
new file mode 100644
index 000000000..1aaefff2b
--- /dev/null
+++ b/src/com/fsck/k9/activity/setup/WelcomeMessage.java
@@ -0,0 +1,33 @@
+package com.fsck.k9.activity.setup;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+import com.fsck.k9.R;
+import com.fsck.k9.activity.K9Activity;
+
+public class WelcomeMessage extends K9Activity implements OnClickListener{
+
+ public static void showWelcomeMessage(Context context) {
+ Intent intent = new Intent(context, WelcomeMessage.class);
+ context.startActivity(intent);
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.welcome_message);
+ ((Button) findViewById(R.id.next)).setOnClickListener(this);
+ }
+
+ public void onClick(View view) {
+ if (view.getId() == R.id.next) {
+ AccountSetupBasics.actionNewAccount(this);
+ finish();
+ }
+ }
+}
diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java
index d12f433ec..ec8b40bc3 100644
--- a/src/com/fsck/k9/controller/MessagingController.java
+++ b/src/com/fsck/k9/controller/MessagingController.java
@@ -2931,6 +2931,9 @@ public class MessagingController implements Runnable {
localFolder.fetch(new Message[] { message }, fp, null);
// Mark that this message is now fully synched
+ if (account.isMarkMessageAsReadOnView()) {
+ message.setFlag(Flag.SEEN, true);
+ }
message.setFlag(Flag.X_DOWNLOADED_FULL, true);
}
diff --git a/src/com/fsck/k9/helper/MessageHelper.java b/src/com/fsck/k9/helper/MessageHelper.java
index 09cfad87e..15394c339 100644
--- a/src/com/fsck/k9/helper/MessageHelper.java
+++ b/src/com/fsck/k9/helper/MessageHelper.java
@@ -85,7 +85,7 @@ public class MessageHelper {
target.uid = message.getUid();
- target.account = account.getDescription();
+ target.account = account.getUuid();
target.uri = "email://messages/" + account.getAccountNumber() + "/" + m.getFolder().getName() + "/" + m.getUid();
} catch (MessagingException me) {
diff --git a/src/com/fsck/k9/helper/Utility.java b/src/com/fsck/k9/helper/Utility.java
index 814692219..27ec953b0 100644
--- a/src/com/fsck/k9/helper/Utility.java
+++ b/src/com/fsck/k9/helper/Utility.java
@@ -211,12 +211,15 @@ public class Utility {
private static final long MILISECONDS_IN_18_HOURS = 18 * 60 * 60 * 1000;
/**
- * Returns true if the specified date is within 18 hours of "now". Returns false otherwise.
- * @param date
- * @return
+ * Check a date to see if it was from "today."
+ * @param date Date of item to check.
+ * @return true if the specified date is within 18 hours of now.
*/
- public static boolean isDateToday(Date date) {
- Date now = new Date();
+ public static boolean isDateToday(final Date date) {
+ if (date == null) {
+ return false;
+ }
+ final Date now = new Date();
if (now.getTime() - MILISECONDS_IN_18_HOURS > date.getTime() || now.getTime() + MILISECONDS_IN_18_HOURS < date.getTime()) {
return false;
} else {
diff --git a/src/com/fsck/k9/mail/internet/BinaryTempFileBody.java b/src/com/fsck/k9/mail/internet/BinaryTempFileBody.java
index 154776ca1..edfae939c 100644
--- a/src/com/fsck/k9/mail/internet/BinaryTempFileBody.java
+++ b/src/com/fsck/k9/mail/internet/BinaryTempFileBody.java
@@ -45,13 +45,16 @@ public class BinaryTempFileBody implements Body {
public void writeTo(OutputStream out) throws IOException, MessagingException {
InputStream in = getInputStream();
- Base64OutputStream base64Out = new Base64OutputStream(out);
try {
- IOUtils.copy(in, base64Out);
+ Base64OutputStream base64Out = new Base64OutputStream(out);
+ try {
+ IOUtils.copy(in, base64Out);
+ } finally {
+ base64Out.close();
+ }
} finally {
- base64Out.close();
+ in.close();
}
- mFile.delete();
}
class BinaryTempFileBodyInputStream extends FilterInputStream {
@@ -61,8 +64,15 @@ public class BinaryTempFileBody implements Body {
@Override
public void close() throws IOException {
+ try {
+ super.close();
+ } finally {
+ mFile.delete();
+ }
+ }
+
+ public void closeWithoutDeleting() throws IOException {
super.close();
- mFile.delete();
}
}
}
diff --git a/src/com/fsck/k9/mail/internet/MimeUtility.java b/src/com/fsck/k9/mail/internet/MimeUtility.java
index 55b360cde..fcdb2e3ce 100644
--- a/src/com/fsck/k9/mail/internet/MimeUtility.java
+++ b/src/com/fsck/k9/mail/internet/MimeUtility.java
@@ -8,6 +8,7 @@ import com.fsck.k9.R;
import com.fsck.k9.helper.HtmlConverter;
import com.fsck.k9.mail.*;
import com.fsck.k9.mail.Message.RecipientType;
+import com.fsck.k9.mail.internet.BinaryTempFileBody.BinaryTempFileBodyInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.james.mime4j.codec.Base64InputStream;
@@ -15,7 +16,6 @@ import org.apache.james.mime4j.codec.QuotedPrintableInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
@@ -1039,19 +1039,34 @@ public class MimeUtility {
* determine the charset from HTML message.
*/
if (mimeType.equalsIgnoreCase("text/html") && charset == null) {
- InputStreamReader in = new InputStreamReader(part.getBody().getInputStream(),
- "US-ASCII");
- char[] buf = new char[256];
- in.read(buf, 0, buf.length);
- String str = new String(buf);
+ InputStream in = part.getBody().getInputStream();
+ try {
+ byte[] buf = new byte[256];
+ in.read(buf, 0, buf.length);
+ String str = new String(buf, "US-ASCII");
- if (str.length() == 0) {
- return "";
- }
- Pattern p = Pattern.compile("", Pattern.CASE_INSENSITIVE);
- Matcher m = p.matcher(str);
- if (m.find()) {
- charset = m.group(1);
+ if (str.length() == 0) {
+ return "";
+ }
+ Pattern p = Pattern.compile("", Pattern.CASE_INSENSITIVE);
+ Matcher m = p.matcher(str);
+ if (m.find()) {
+ charset = m.group(1);
+ }
+ } finally {
+ try {
+ if (in instanceof BinaryTempFileBodyInputStream) {
+ /*
+ * If this is a BinaryTempFileBodyInputStream, calling close()
+ * will delete the file. But we can't let that happen because
+ * the file needs to be opened again by the code a few lines
+ * down.
+ */
+ ((BinaryTempFileBodyInputStream) in).closeWithoutDeleting();
+ } else {
+ in.close();
+ }
+ } catch (Exception e) { /* ignore */ }
}
}
charset = fixupCharset(charset, getMessageFromPart(part));
@@ -1061,7 +1076,23 @@ public class MimeUtility {
* the stream is now wrapped we'll remove any transfer encoding at this point.
*/
InputStream in = part.getBody().getInputStream();
- return readToString(in, charset);
+ try {
+ String text = readToString(in, charset);
+
+ // Replace the body with a TextBody that already contains the decoded text
+ part.setBody(new TextBody(text));
+
+ return text;
+ } finally {
+ try {
+ /*
+ * This time we don't care if it's a BinaryTempFileBodyInputStream. We
+ * replaced the body with a TextBody instance and hence don't need the
+ * file anymore.
+ */
+ in.close();
+ } catch (IOException e) { /* Ignore */ }
+ }
}
}
diff --git a/src/com/fsck/k9/mail/store/ImapStore.java b/src/com/fsck/k9/mail/store/ImapStore.java
index 0cd575ce3..7c6b47f5b 100644
--- a/src/com/fsck/k9/mail/store/ImapStore.java
+++ b/src/com/fsck/k9/mail/store/ImapStore.java
@@ -683,7 +683,8 @@ public class ImapStore extends Store {
} else if (attribute.equals("\\Sent")) {
mAccount.setSentFolderName(decodedFolderName);
if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected sent folder: " + decodedFolderName);
- } else if (attribute.equals("\\Spam")) {
+ } else if (attribute.equals("\\Spam") || attribute.equals("\\Junk")) {
+ //rfc6154 just mentions \Junk
mAccount.setSpamFolderName(decodedFolderName);
if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected spam folder: " + decodedFolderName);
} else if (attribute.equals("\\Trash")) {
diff --git a/src/com/fsck/k9/mail/store/LocalStore.java b/src/com/fsck/k9/mail/store/LocalStore.java
index a3403d670..774a83566 100644
--- a/src/com/fsck/k9/mail/store/LocalStore.java
+++ b/src/com/fsck/k9/mail/store/LocalStore.java
@@ -82,21 +82,6 @@ public class LocalStore extends Store implements Serializable {
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.X_DESTROYED, Flag.SEEN, Flag.FLAGGED };
- private static final Set HEADERS_TO_SAVE;
- static {
- Set set = new HashSet();
- set.add(K9.IDENTITY_HEADER);
- set.add("To");
- set.add("Cc");
- set.add("From");
- set.add("In-Reply-To");
- set.add("References");
- set.add(MimeHeader.HEADER_CONTENT_ID);
- set.add(MimeHeader.HEADER_CONTENT_DISPOSITION);
- set.add("User-Agent");
- HEADERS_TO_SAVE = Collections.unmodifiableSet(set);
- }
-
/*
* a String containing the columns getMessages expects to work with
* in the correct order.
@@ -2506,12 +2491,9 @@ Log.v("ASH", mAccount.getDescription() + ":" + name + " is " + (localOnly == 1 ?
database.execute(true, new DbCallback() {
@Override
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
- boolean saveAllHeaders = mAccount.saveAllHeaders();
- boolean gotAdditionalHeaders = false;
deleteHeaders(id);
for (String name : message.getHeaderNames()) {
- if (saveAllHeaders || HEADERS_TO_SAVE.contains(name)) {
String[] values = message.getHeader(name);
for (String value : values) {
ContentValues cv = new ContentValues();
@@ -2520,22 +2502,17 @@ Log.v("ASH", mAccount.getDescription() + ":" + name + " is " + (localOnly == 1 ?
cv.put("value", value);
db.insert("headers", "name", cv);
}
- } else {
- gotAdditionalHeaders = true;
- }
}
- if (!gotAdditionalHeaders) {
- // Remember that all headers for this message have been saved, so it is
- // not necessary to download them again in case the user wants to see all headers.
- List appendedFlags = new ArrayList();
- appendedFlags.addAll(Arrays.asList(message.getFlags()));
- appendedFlags.add(Flag.X_GOT_ALL_HEADERS);
+ // Remember that all headers for this message have been saved, so it is
+ // not necessary to download them again in case the user wants to see all headers.
+ List appendedFlags = new ArrayList();
+ appendedFlags.addAll(Arrays.asList(message.getFlags()));
+ appendedFlags.add(Flag.X_GOT_ALL_HEADERS);
- db.execSQL("UPDATE messages " + "SET flags = ? " + " WHERE id = ?",
- new Object[]
- { Utility.combine(appendedFlags.toArray(), ',').toUpperCase(Locale.US), id });
- }
+ db.execSQL("UPDATE messages " + "SET flags = ? " + " WHERE id = ?",
+ new Object[]
+ { Utility.combine(appendedFlags.toArray(), ',').toUpperCase(Locale.US), id });
return null;
}
});
diff --git a/src/com/fsck/k9/preferences/AccountSettings.java b/src/com/fsck/k9/preferences/AccountSettings.java
index 24254339b..4fcdd7f04 100644
--- a/src/com/fsck/k9/preferences/AccountSettings.java
+++ b/src/com/fsck/k9/preferences/AccountSettings.java
@@ -162,9 +162,6 @@ public class AccountSettings {
s.put("ringtone", Settings.versions(
new V(1, new RingtoneSetting("content://settings/system/notification_sound"))
));
- s.put("saveAllHeaders", Settings.versions(
- new V(1, new BooleanSetting(true))
- ));
s.put("autoUploadOnMove", Settings.versions(
new V(1, new BooleanSetting(true))
));
diff --git a/src/com/fsck/k9/preferences/GlobalSettings.java b/src/com/fsck/k9/preferences/GlobalSettings.java
index cb05b7e12..9eed3e5e7 100644
--- a/src/com/fsck/k9/preferences/GlobalSettings.java
+++ b/src/com/fsck/k9/preferences/GlobalSettings.java
@@ -140,9 +140,6 @@ public class GlobalSettings {
s.put("language", Settings.versions(
new V(1, new LanguageSetting())
));
- s.put("manageBack", Settings.versions(
- new V(1, new BooleanSetting(false))
- ));
s.put("measureAccounts", Settings.versions(
new V(1, new BooleanSetting(true))
));
@@ -155,9 +152,6 @@ public class GlobalSettings {
s.put("messageListStars", Settings.versions(
new V(1, new BooleanSetting(true))
));
- s.put("messageListTouchable", Settings.versions(
- new V(1, new BooleanSetting(false))
- ));
s.put("messageViewFixedWidthFont", Settings.versions(
new V(1, new BooleanSetting(false))
));
diff --git a/src/com/fsck/k9/preferences/Settings.java b/src/com/fsck/k9/preferences/Settings.java
index 392987b1f..99232af10 100644
--- a/src/com/fsck/k9/preferences/Settings.java
+++ b/src/com/fsck/k9/preferences/Settings.java
@@ -35,7 +35,7 @@ public class Settings {
*
* @see SettingsExporter
*/
- public static final int VERSION = 14;
+ public static final int VERSION = 15;
public static Map validate(int version, Map> settings,
diff --git a/src/com/fsck/k9/provider/MessageProvider.java b/src/com/fsck/k9/provider/MessageProvider.java
index 0557575af..b8029c1d0 100644
--- a/src/com/fsck/k9/provider/MessageProvider.java
+++ b/src/com/fsck/k9/provider/MessageProvider.java
@@ -28,6 +28,7 @@ import com.fsck.k9.activity.MessageList;
import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.controller.MessagingListener;
import com.fsck.k9.helper.MessageHelper;
+import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
@@ -81,7 +82,31 @@ public class MessageProvider extends ContentProvider {
*/
String UNREAD = "unread";
+ /**
+ * Type: TEXT
+ */
String ACCOUNT = "account";
+
+ /**
+ * Type: INTEGER
+ */
+ String ACCOUNT_NUMBER = "accountNumber";
+
+ /**
+ * Type: BOOLEAN
+ */
+ String HAS_ATTACHMENTS = "hasAttachments";
+
+ /**
+ * Type: BOOLEAN
+ */
+ String HAS_STAR = "hasStar";
+
+ /**
+ * Type: INTEGER
+ */
+ String ACCOUNT_COLOR = "accountColor";
+
String URI = "uri";
String DELETE_URI = "delUri";
@@ -196,7 +221,35 @@ public class MessageProvider extends ContentProvider {
return source.message.getFolder().getAccount().getDescription();
}
}
-
+
+ public static class AccountColorExtractor implements FieldExtractor {
+ @Override
+ public Integer getField(final MessageInfoHolder source) {
+ return source.message.getFolder().getAccount().getChipColor();
+ }
+ }
+
+ public static class AccountNumberExtractor implements FieldExtractor {
+ @Override
+ public Integer getField(final MessageInfoHolder source) {
+ return source.message.getFolder().getAccount().getAccountNumber();
+ }
+ }
+
+ public static class HasAttachmentsExtractor implements FieldExtractor {
+ @Override
+ public Boolean getField(final MessageInfoHolder source) {
+ return source.message.hasAttachments();
+ }
+ }
+
+ public static class HasStarExtractor implements FieldExtractor {
+ @Override
+ public Boolean getField(final MessageInfoHolder source) {
+ return source.message.isSet(Flag.FLAGGED);
+ }
+ }
+
public static class UnreadExtractor implements FieldExtractor {
@Override
public Boolean getField(final MessageInfoHolder source) {
@@ -311,10 +364,18 @@ public class MessageProvider extends ContentProvider {
extractors.put(field, new UriExtractor());
} else if (MessageColumns.DELETE_URI.equals(field)) {
extractors.put(field, new DeleteUriExtractor());
- } else if (MessageColumns.ACCOUNT.equals(field)) {
- extractors.put(field, new AccountExtractor());
} else if (MessageColumns.UNREAD.equals(field)) {
extractors.put(field, new UnreadExtractor());
+ } else if (MessageColumns.ACCOUNT.equals(field)) {
+ extractors.put(field, new AccountExtractor());
+ } else if (MessageColumns.ACCOUNT_COLOR.equals(field)) {
+ extractors.put(field, new AccountColorExtractor());
+ } else if (MessageColumns.ACCOUNT_NUMBER.equals(field)) {
+ extractors.put(field, new AccountNumberExtractor());
+ } else if (MessageColumns.HAS_ATTACHMENTS.equals(field)) {
+ extractors.put(field, new HasAttachmentsExtractor());
+ } else if (MessageColumns.HAS_STAR.equals(field)) {
+ extractors.put(field, new HasStarExtractor());
} else if (MessageColumns.INCREMENT.equals(field)) {
extractors.put(field, new IncrementExtractor());
}
@@ -328,7 +389,11 @@ public class MessageProvider extends ContentProvider {
* Retrieve the account list.
*/
protected class AccountsQueryHandler implements QueryHandler {
-
+ private static final String FIELD_ACCOUNT_NUMBER = "accountNumber";
+ private static final String FIELD_ACCOUNT_NAME = "accountName";
+ private static final String FIELD_ACCOUNT_UUID = "accountUuid";
+ private static final String FIELD_ACCOUNT_COLOR = "accountColor";
+
@Override
public String getPath() {
return "accounts";
@@ -337,18 +402,39 @@ public class MessageProvider extends ContentProvider {
@Override
public Cursor query(final Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws Exception {
- return getAllAccounts();
+ return getAllAccounts(projection);
}
- public Cursor getAllAccounts() {
- String[] projection = new String[] { "accountNumber", "accountName" };
+ public Cursor getAllAccounts(String[] projection) {
+ // Default projection
+ if(projection == null) {
+ projection = new String[] { FIELD_ACCOUNT_NUMBER, FIELD_ACCOUNT_NAME };
+ }
+
MatrixCursor ret = new MatrixCursor(projection);
for (Account account : Preferences.getPreferences(getContext()).getAccounts()) {
- Object[] values = new Object[2];
- values[0] = account.getAccountNumber();
- values[1] = account.getDescription();
+ Object[] values = new Object[projection.length];
+
+ // Build account row
+ int fieldIndex = 0;
+ for(String field : projection) {
+
+ if(FIELD_ACCOUNT_NUMBER.equals(field)) {
+ values[fieldIndex] = account.getAccountNumber();
+ } else if(FIELD_ACCOUNT_NAME.equals(field)) {
+ values[fieldIndex] = account.getDescription();
+ } else if(FIELD_ACCOUNT_UUID.equals(field)) {
+ values[fieldIndex] = account.getUuid();
+ } else if(FIELD_ACCOUNT_COLOR.equals(field)) {
+ values[fieldIndex] = account.getChipColor();
+ } else {
+ values[fieldIndex] = null;
+ }
+ ++fieldIndex;
+ }
+
ret.addRow(values);
}
@@ -879,7 +965,7 @@ public class MessageProvider extends ContentProvider {
MessagingController.getInstance(application).addListener(new MessagingListener() {
@Override
- public void searchStats(final AccountStats stats) {
+ public void folderStatusChanged(Account account, String folderName, int unreadMessageCount) {
application.getContentResolver().notifyChange(CONTENT_URI, null);
}
});
diff --git a/src/com/fsck/k9/provider/UnreadWidgetProvider.java b/src/com/fsck/k9/provider/UnreadWidgetProvider.java
index 616707c34..2af479d15 100644
--- a/src/com/fsck/k9/provider/UnreadWidgetProvider.java
+++ b/src/com/fsck/k9/provider/UnreadWidgetProvider.java
@@ -59,7 +59,8 @@ public class UnreadWidgetProvider extends AppWidgetProvider {
unreadCount = stats.unreadMessageCount;
accountName = account.getDescription();
if (K9.FOLDER_NONE.equals(account.getAutoExpandFolderName())) {
- clickIntent = FolderList.actionHandleAccountIntent(context, account, null);
+ clickIntent = FolderList.actionHandleAccountIntent(context, account, null,
+ false);
} else {
clickIntent = MessageList.actionHandleFolderIntent(context, account,
account.getAutoExpandFolderName());
diff --git a/src/com/fsck/k9/view/ColorChip.java b/src/com/fsck/k9/view/ColorChip.java
index 4bdc9e356..77d190267 100644
--- a/src/com/fsck/k9/view/ColorChip.java
+++ b/src/com/fsck/k9/view/ColorChip.java
@@ -1,5 +1,6 @@
package com.fsck.k9.view;
+import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.PathShape;
@@ -9,28 +10,27 @@ public class ColorChip {
static {
- CHIP_PATH.lineTo(6, 0);
-
- CHIP_PATH.cubicTo(8f, 0f, 10f, 0f, 10f, 2f);
- CHIP_PATH.lineTo(10, 8);
- CHIP_PATH.cubicTo(10f, 9f, 10f, 10f, 6f, 10f);
- CHIP_PATH.lineTo(0, 10);
+ CHIP_PATH.addCircle(8,8,7f,Path.Direction.CW);
CHIP_PATH.close();
}
private ShapeDrawable mDrawable;
- public ColorChip(int color) {
+ public ColorChip(int color, boolean messageRead) {
- mDrawable = new ShapeDrawable(new PathShape(CHIP_PATH, 10, 10));
+ mDrawable = new ShapeDrawable(new PathShape(CHIP_PATH, 16f, 16f));
+ mDrawable.getPaint().setStrokeWidth(2);
+ if (messageRead) {
+ // Read messages get an outlined circle
+ mDrawable.getPaint().setStyle(Paint.Style.STROKE);
+ }
mDrawable.getPaint().setColor(color);
}
public ShapeDrawable drawable() {
-
return mDrawable;
}
diff --git a/src/com/fsck/k9/view/MessageHeader.java b/src/com/fsck/k9/view/MessageHeader.java
index d2f55b345..4a3aa549a 100644
--- a/src/com/fsck/k9/view/MessageHeader.java
+++ b/src/com/fsck/k9/view/MessageHeader.java
@@ -25,6 +25,7 @@ import com.fsck.k9.R;
import com.fsck.k9.helper.Contacts;
import com.fsck.k9.Account;
import com.fsck.k9.helper.DateFormatter;
+import com.fsck.k9.helper.StringUtils;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Message;
@@ -237,8 +238,8 @@ public class MessageHeader extends ScrollView implements OnClickListener {
mAccount = account;
initializeLayout();
- String subject = message.getSubject();
- if (subject == null || subject.equals("")) {
+ final String subject = message.getSubject();
+ if (StringUtils.isNullOrEmpty(subject)) {
mSubjectView.setText(mContext.getText(R.string.general_no_subject));
} else {
mSubjectView.setText(subject);
@@ -432,4 +433,11 @@ public class MessageHeader extends ScrollView implements OnClickListener {
mOnLayoutChangedListener.onLayoutChanged();
}
}
+
+ /**
+ * The subject line defaults to GONE. Make it visible.
+ */
+ public void showSubjectLine() {
+ mSubjectView.setVisibility(VISIBLE);
+ }
}
diff --git a/src/com/fsck/k9/view/MessageTitleView.java b/src/com/fsck/k9/view/MessageTitleView.java
new file mode 100644
index 000000000..96a3b23d3
--- /dev/null
+++ b/src/com/fsck/k9/view/MessageTitleView.java
@@ -0,0 +1,55 @@
+package com.fsck.k9.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.TextView;
+import com.fsck.k9.K9;
+
+/**
+ * This {@link TextView} is used in the title of the {@link com.fsck.k9.activity.MessageView} ActionBar.
+ * It'll un-hide the subject line {@link MessageHeader} if it doesn't fit in the ActionBar's title area.
+ */
+public class MessageTitleView extends TextView {
+ private static final String LOG_PREFIX = "MessageTitleView: ";
+ private MessageHeader mHeader;
+
+ public MessageTitleView(Context context) {
+ this(context, null);
+ }
+
+ public MessageTitleView(Context context, AttributeSet attrs) {
+ this(context, attrs, android.R.attr.textViewStyle);
+ }
+
+ public MessageTitleView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * Check to see if we need to unhide the subject line in the MessageHeader or not.
+ * @param canvas Canvas to draw on.
+ */
+ @Override
+ public void onDraw(Canvas canvas) {
+ if(mHeader != null && getLayout() != null) {
+ if(getLayout().getEllipsisCount(1) > 0) {
+ if(K9.DEBUG) {
+ Log.d(K9.LOG_TAG, LOG_PREFIX +
+ "Subject was truncated; enabling the subject line in the message header.");
+ }
+ mHeader.showSubjectLine();
+ } else {
+ if (K9.DEBUG) {
+ Log.d(K9.LOG_TAG, LOG_PREFIX + "Subject was fully shown in ActionBar.");
+ }
+ }
+ }
+ super.onDraw(canvas);
+ }
+
+ public void setMessageHeader(final MessageHeader header) {
+ this.mHeader = header;
+ }
+}
diff --git a/src/com/fsck/k9/view/SingleMessageView.java b/src/com/fsck/k9/view/SingleMessageView.java
index aa8b29984..4e32a6c71 100644
--- a/src/com/fsck/k9/view/SingleMessageView.java
+++ b/src/com/fsck/k9/view/SingleMessageView.java
@@ -495,6 +495,15 @@ public class SingleMessageView extends LinearLayout implements OnClickListener,
mShowAttachmentsAction.setVisibility(show ? View.VISIBLE : View.GONE);
}
+ /**
+ * Fetch the message header view. This is not the same as the message headers; this is the View shown at the top
+ * of messages.
+ * @return MessageHeader View.
+ */
+ public MessageHeader getMessageHeaderView() {
+ return mHeaderContainer;
+ }
+
public void setHeaders(final Message message, Account account) {
try {
mHeaderContainer.populate(message, account);