1957 lines
77 KiB
Java
1957 lines
77 KiB
Java
/*
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.email.activity;
|
|
|
|
import com.android.email.Controller;
|
|
import com.android.email.Email;
|
|
import com.android.email.MessagingController;
|
|
import com.android.email.MessagingListener;
|
|
import com.android.email.R;
|
|
import com.android.email.Utility;
|
|
import com.android.email.activity.setup.AccountSettings;
|
|
import com.android.email.mail.Address;
|
|
import com.android.email.mail.AuthenticationFailedException;
|
|
import com.android.email.mail.CertificateValidationException;
|
|
import com.android.email.mail.Flag;
|
|
import com.android.email.mail.Message;
|
|
import com.android.email.mail.MessagingException;
|
|
import com.android.email.mail.Message.RecipientType;
|
|
import com.android.email.mail.store.LocalStore;
|
|
import com.android.email.mail.store.LocalStore.LocalMessage;
|
|
import com.android.email.provider.EmailContent;
|
|
import com.android.email.provider.EmailContent.Mailbox;
|
|
import com.android.email.provider.EmailContent.MailboxColumns;
|
|
|
|
import android.app.ExpandableListActivity;
|
|
import android.app.NotificationManager;
|
|
import android.content.ContentUris;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.database.Cursor;
|
|
import android.graphics.Typeface;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.net.Uri;
|
|
import android.os.AsyncTask;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.os.Process;
|
|
import android.util.Config;
|
|
import android.util.Log;
|
|
import android.view.ContextMenu;
|
|
import android.view.LayoutInflater;
|
|
import android.view.Menu;
|
|
import android.view.MenuItem;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.Window;
|
|
import android.view.ContextMenu.ContextMenuInfo;
|
|
import android.widget.BaseExpandableListAdapter;
|
|
import android.widget.CursorTreeAdapter;
|
|
import android.widget.ExpandableListView;
|
|
import android.widget.ProgressBar;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
|
|
|
|
import java.text.DateFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Date;
|
|
|
|
/**
|
|
* FolderMessageList is the primary user interface for the program. This Activity shows
|
|
* a two level list of the Account's folders and each folder's messages. From this
|
|
* Activity the user can perform all standard message operations.
|
|
*
|
|
*
|
|
* TODO (old)
|
|
* some things that are slowing us down:
|
|
* Need a way to remove state such as progress bar and per folder progress on
|
|
* resume if the command has completed.
|
|
*
|
|
* TODO (old)
|
|
* Break out seperate functions for:
|
|
* refresh local folders
|
|
* refresh remote folders
|
|
* refresh open folder local messages
|
|
* refresh open folder remote messages
|
|
*
|
|
* And don't refresh remote folders ever unless the user runs a refresh. Maybe not even then.
|
|
*
|
|
* TODO (new - 2009 rebuild)
|
|
* From old listFolders() handler - this now should happen with the groups (folder) cursor
|
|
* reports a data change:
|
|
* Now we need to refresh any folders that are currently expanded. We do this
|
|
* in case the status or amount of messages has changed.
|
|
*/
|
|
public class FolderMessageList extends ExpandableListActivity {
|
|
|
|
private static final boolean DBG_ADD_CONTENT = false; // DO NOT CHECK IN AS 'TRUE'
|
|
private static final boolean DBG_LOG_CURSORS = false; // DO NOT CHECK IN AS 'TRUE'
|
|
|
|
private static final String EXTRA_ACCOUNT_ID = "account";
|
|
private static final String EXTRA_CLEAR_NOTIFICATION = "clearNotification";
|
|
private static final String EXTRA_INITIAL_FOLDER = "initialFolder";
|
|
|
|
private static final String STATE_KEY_LIST =
|
|
"com.android.email.activity.folderlist_expandableListState";
|
|
private static final String STATE_KEY_EXPANDED_GROUP =
|
|
"com.android.email.activity.folderlist_expandedGroup";
|
|
private static final String STATE_KEY_EXPANDED_GROUP_SELECTION =
|
|
"com.android.email.activity.folderlist_expandedGroupSelection";
|
|
private static final String STATE_KEY_REFRESH_REMOTE =
|
|
"com.android.email.activity.refresh_remote";
|
|
|
|
private static final int UPDATE_FOLDER_ON_EXPAND_INTERVAL_MS = (1000 * 60 * 3);
|
|
|
|
private static final int[] colorChipResIds = new int[] {
|
|
R.drawable.appointment_indicator_leftside_1,
|
|
R.drawable.appointment_indicator_leftside_2,
|
|
R.drawable.appointment_indicator_leftside_3,
|
|
R.drawable.appointment_indicator_leftside_4,
|
|
R.drawable.appointment_indicator_leftside_5,
|
|
R.drawable.appointment_indicator_leftside_6,
|
|
R.drawable.appointment_indicator_leftside_7,
|
|
R.drawable.appointment_indicator_leftside_8,
|
|
R.drawable.appointment_indicator_leftside_9,
|
|
R.drawable.appointment_indicator_leftside_10,
|
|
R.drawable.appointment_indicator_leftside_11,
|
|
R.drawable.appointment_indicator_leftside_12,
|
|
R.drawable.appointment_indicator_leftside_13,
|
|
R.drawable.appointment_indicator_leftside_14,
|
|
R.drawable.appointment_indicator_leftside_15,
|
|
R.drawable.appointment_indicator_leftside_16,
|
|
R.drawable.appointment_indicator_leftside_17,
|
|
R.drawable.appointment_indicator_leftside_18,
|
|
R.drawable.appointment_indicator_leftside_19,
|
|
R.drawable.appointment_indicator_leftside_20,
|
|
R.drawable.appointment_indicator_leftside_21,
|
|
};
|
|
|
|
private static final Flag[] DELETED_FLAG = new Flag[] { Flag.DELETED };
|
|
|
|
private ExpandableListView mListView;
|
|
private int colorChipResId;
|
|
|
|
private LoadMailBoxesTask mLoadMailboxesTask;
|
|
private LoadMessagesTask mLoadMessagesTask;
|
|
private NewMessagingListener mMessagingListener;
|
|
private NewFolderMessageListAdapter mNewAdapter;
|
|
private ControllerResults mControllerCallback;
|
|
|
|
private FolderMessageListAdapter mAdapter;
|
|
private LayoutInflater mInflater;
|
|
private long mAccountId;
|
|
private EmailContent.Account mAccount;
|
|
private boolean mSyncWindowUser;
|
|
|
|
/**
|
|
* Stores the name of the folder that we want to open as soon as possible after load. It is
|
|
* set to null once the folder has been opened once.
|
|
*/
|
|
private String mInitialFolder;
|
|
|
|
private java.text.DateFormat mDateFormat;
|
|
private java.text.DateFormat mTimeFormat;
|
|
|
|
private int mExpandedGroup = -1;
|
|
private boolean mRestoringState;
|
|
|
|
private boolean mRefreshRemote;
|
|
|
|
/**
|
|
* These arrays support NewFolderMessageListAdapter
|
|
*/
|
|
private final static int[] sGroupToIds = new int[] {
|
|
R.id.folder_name,
|
|
R.id.folder_status,
|
|
R.id.new_message_count
|
|
};
|
|
private final static int[] sChildToIds = new int[] {
|
|
R.id.chip,
|
|
R.id.from,
|
|
R.id.date,
|
|
R.id.subject
|
|
};
|
|
|
|
private FolderMessageListHandler mHandler = new FolderMessageListHandler();
|
|
|
|
class FolderMessageListHandler extends Handler {
|
|
private static final int MSG_PROGRESS = 2;
|
|
private static final int MSG_DATA_CHANGED = 3;
|
|
private static final int MSG_FOLDER_LOADING = 7;
|
|
private static final int MSG_REMOVE_MESSAGE = 11;
|
|
private static final int MSG_SYNC_MESSAGES = 13;
|
|
private static final int MSG_FOLDER_STATUS = 17;
|
|
private static final int MSG_NEW_MESSAGE = 19;
|
|
private static final int MSG_REMOVE_MESSAGE_UID = 20;
|
|
private static final int MSG_MESSAGE_UID_CHANGED = 21;
|
|
private static final int MSG_LIST_FOLDERS_FINISHED = 22;
|
|
|
|
@Override
|
|
public void handleMessage(android.os.Message msg) {
|
|
switch (msg.what) {
|
|
case MSG_PROGRESS:
|
|
setProgressBarIndeterminateVisibility(msg.arg1 != 0);
|
|
break;
|
|
case MSG_DATA_CHANGED:
|
|
mAdapter.notifyDataSetChanged();
|
|
break;
|
|
/*
|
|
* The following functions modify the state of the adapter's underlying list and
|
|
* must be run here, in the main thread, so that notifyDataSetChanged is run
|
|
* before any further requests are made to the adapter.
|
|
*/
|
|
case MSG_FOLDER_LOADING: {
|
|
FolderInfoHolder folder = mAdapter.getFolder((String) msg.obj);
|
|
if (folder != null) {
|
|
folder.loading = msg.arg1 != 0;
|
|
mAdapter.notifyDataSetChanged();
|
|
}
|
|
break;
|
|
}
|
|
case MSG_REMOVE_MESSAGE: {
|
|
FolderInfoHolder folder = (FolderInfoHolder) ((Object[]) msg.obj)[0];
|
|
MessageInfoHolder message = (MessageInfoHolder) ((Object[]) msg.obj)[1];
|
|
folder.messages.remove(message);
|
|
mAdapter.notifyDataSetChanged();
|
|
break;
|
|
}
|
|
case MSG_SYNC_MESSAGES: {
|
|
String folderName = (String) ((Object[]) msg.obj)[0];
|
|
Message[] messages = (Message[]) ((Object[]) msg.obj)[1];
|
|
FolderInfoHolder folder = mAdapter.getFolder(folderName);
|
|
if (folder == null) {
|
|
return;
|
|
}
|
|
folder.messages.clear();
|
|
for (Message message : messages) {
|
|
mAdapter.addOrUpdateMessage(folder, message, false, false);
|
|
}
|
|
Collections.sort(folder.messages);
|
|
mAdapter.notifyDataSetChanged();
|
|
break;
|
|
}
|
|
case MSG_FOLDER_STATUS: {
|
|
String folderName = (String) ((Object[]) msg.obj)[0];
|
|
String status = (String) ((Object[]) msg.obj)[1];
|
|
int forceRefresh = msg.arg1;
|
|
FolderInfoHolder folder = mAdapter.getFolder(folderName);
|
|
if (folder != null) {
|
|
folder.status = status;
|
|
if (forceRefresh != 0) {
|
|
/*
|
|
* Reset the last checked time to 0 so that the next expand will
|
|
* attempt to refresh this folder.
|
|
*/
|
|
folder.lastChecked = 0;
|
|
}
|
|
mAdapter.notifyDataSetChanged();
|
|
}
|
|
break;
|
|
}
|
|
case MSG_NEW_MESSAGE: {
|
|
String folder = (String) ((Object[]) msg.obj)[0];
|
|
Message message = (Message) ((Object[]) msg.obj)[1];
|
|
mAdapter.addOrUpdateMessage(folder, message);
|
|
break;
|
|
}
|
|
case MSG_REMOVE_MESSAGE_UID: {
|
|
String folder = (String) ((Object[]) msg.obj)[0];
|
|
String messageUid = (String) ((Object[]) msg.obj)[1];
|
|
mAdapter.removeMessage(folder, messageUid);
|
|
break;
|
|
}
|
|
case MSG_MESSAGE_UID_CHANGED: {
|
|
String folder = (String) ((Object[]) msg.obj)[0];
|
|
String oldUid = (String) ((Object[]) msg.obj)[1];
|
|
String newUid = (String) ((Object[]) msg.obj)[2];
|
|
FolderInfoHolder holder = mAdapter.getFolder(folder);
|
|
if (folder != null) {
|
|
for (MessageInfoHolder message : holder.messages) {
|
|
if (message.uid.equals(oldUid)) {
|
|
message.uid = newUid;
|
|
message.message.setUid(newUid);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case MSG_LIST_FOLDERS_FINISHED: {
|
|
doRefreshOpenMailbox();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
super.handleMessage(msg);
|
|
}
|
|
}
|
|
|
|
public void synchronizeMessages(String folder, Message[] messages) {
|
|
android.os.Message msg = android.os.Message.obtain();
|
|
msg.what = MSG_SYNC_MESSAGES;
|
|
msg.obj = new Object[] { folder, messages };
|
|
sendMessage(msg);
|
|
}
|
|
|
|
public void removeMessage(FolderInfoHolder folder, MessageInfoHolder message) {
|
|
android.os.Message msg = new android.os.Message();
|
|
msg.what = MSG_REMOVE_MESSAGE;
|
|
msg.obj = new Object[] { folder, message };
|
|
sendMessage(msg);
|
|
}
|
|
|
|
public void folderLoading(String folder, boolean loading) {
|
|
android.os.Message msg = new android.os.Message();
|
|
msg.what = MSG_FOLDER_LOADING;
|
|
msg.arg1 = loading ? 1 : 0;
|
|
msg.obj = folder;
|
|
sendMessage(msg);
|
|
}
|
|
|
|
public void progress(boolean progress) {
|
|
android.os.Message msg = new android.os.Message();
|
|
msg.what = MSG_PROGRESS;
|
|
msg.arg1 = progress ? 1 : 0;
|
|
sendMessage(msg);
|
|
}
|
|
|
|
public void dataChanged() {
|
|
sendEmptyMessage(MSG_DATA_CHANGED);
|
|
}
|
|
|
|
public void folderStatus(String folder, String status, boolean forceRefresh) {
|
|
android.os.Message msg = new android.os.Message();
|
|
msg.what = MSG_FOLDER_STATUS;
|
|
msg.obj = new String[] { folder, status };
|
|
msg.arg1 = forceRefresh ? 1 : 0;
|
|
sendMessage(msg);
|
|
}
|
|
|
|
public void newMessage(String folder, Message message) {
|
|
android.os.Message msg = android.os.Message.obtain();
|
|
msg.what = MSG_NEW_MESSAGE;
|
|
msg.obj = new Object[] { folder, message };
|
|
sendMessage(msg);
|
|
}
|
|
|
|
public void removeMessageByUid(String folder, String messageUid) {
|
|
android.os.Message msg = android.os.Message.obtain();
|
|
msg.what = MSG_REMOVE_MESSAGE_UID;
|
|
msg.obj = new Object[] { folder, messageUid };
|
|
sendMessage(msg);
|
|
}
|
|
|
|
public void messageUidChanged(String folder, String oldUid, String newUid) {
|
|
android.os.Message msg = android.os.Message.obtain();
|
|
msg.what = MSG_MESSAGE_UID_CHANGED;
|
|
msg.obj = new Object[] { folder, oldUid, newUid };
|
|
sendMessage(msg);
|
|
}
|
|
|
|
public void listFoldersFinished() {
|
|
sendEmptyMessage(MSG_LIST_FOLDERS_FINISHED);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class is responsible for reloading the list of local messages for a given folder,
|
|
* notifying the adapter that the message have been loaded and queueing up a remote
|
|
* update of the folder.
|
|
*/
|
|
static class FolderUpdateWorker implements Runnable {
|
|
String mFolder;
|
|
boolean mSynchronizeRemote;
|
|
MessagingListener mListener;
|
|
EmailContent.Account mAccount;
|
|
MessagingController mController;
|
|
|
|
/**
|
|
* Create a worker for the given folder and specifying whether the
|
|
* worker should synchronize the remote folder or just the local one.
|
|
* @param folder
|
|
* @param synchronizeRemote
|
|
*/
|
|
public FolderUpdateWorker(String folder, boolean synchronizeRemote,
|
|
MessagingListener listener, EmailContent.Account account,
|
|
MessagingController controller) {
|
|
mFolder = folder;
|
|
mSynchronizeRemote = synchronizeRemote;
|
|
mListener = listener;
|
|
mAccount = account;
|
|
mController = controller;
|
|
}
|
|
|
|
public void run() {
|
|
// Lower our priority
|
|
// Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
|
// Synchronously load the list of local messages
|
|
// mController.listLocalMessages(
|
|
// mAccount,
|
|
// mFolder,
|
|
// mListener);
|
|
// if (mSynchronizeRemote) {
|
|
// // Tell the MessagingController to run a remote update of this folder
|
|
// // at it's leisure
|
|
// mController.synchronizeMailbox(
|
|
// mAccount,
|
|
// mFolder,
|
|
// mListener);
|
|
// }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Open a specific account.
|
|
* @param context
|
|
* @param id
|
|
*/
|
|
@Deprecated
|
|
public static void actionHandleAccount(Context context, long id) {
|
|
actionHandleAccount(context, id, null);
|
|
}
|
|
|
|
/**
|
|
* Open a specific account.
|
|
* @param context
|
|
* @param id
|
|
* @param initialFolder The folder to open, or null for none
|
|
*/
|
|
@Deprecated
|
|
public static void actionHandleAccount(Context context, long id, String initialFolder) {
|
|
Intent intent = new Intent(context, FolderMessageList.class);
|
|
intent.putExtra(EXTRA_ACCOUNT_ID, id);
|
|
if (initialFolder != null) {
|
|
intent.putExtra(EXTRA_INITIAL_FOLDER, initialFolder);
|
|
}
|
|
context.startActivity(intent);
|
|
}
|
|
|
|
@Deprecated
|
|
public static Intent actionHandleAccountIntent(Context context, long id, String initialFolder) {
|
|
Intent intent = new Intent(context, FolderMessageList.class);
|
|
intent.putExtra(EXTRA_ACCOUNT_ID, id);
|
|
intent.putExtra(EXTRA_CLEAR_NOTIFICATION, true);
|
|
if (initialFolder != null) {
|
|
intent.putExtra(EXTRA_INITIAL_FOLDER, initialFolder);
|
|
}
|
|
return intent;
|
|
}
|
|
|
|
/**
|
|
* This should be used for generating lightweight (Uri-only) intents. It probably makes sense
|
|
* to move entirely to this, and stop passing entire account structs through Intents.
|
|
*
|
|
* @param context Calling context for building the intent
|
|
* @param account The account of interest
|
|
* @param initialFolder If non-null, can set the folder name to open (typically Email.INBOX)
|
|
* @return an Intent which can be used to view that account
|
|
*/
|
|
@Deprecated
|
|
public static Intent actionHandleAccountUriIntent(Context context, long id,
|
|
String initialFolder) {
|
|
Intent i = actionHandleAccountIntent(context, id, initialFolder);
|
|
i.removeExtra(EXTRA_ACCOUNT_ID);
|
|
Uri uri = ContentUris.withAppendedId(EmailContent.Account.CONTENT_URI, id);
|
|
i.setData(uri);
|
|
return i;
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
|
|
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
|
|
|
mDateFormat = android.text.format.DateFormat.getDateFormat(this); // short format
|
|
mTimeFormat = android.text.format.DateFormat.getTimeFormat(this); // 12/24 date format
|
|
|
|
mListView = getExpandableListView();
|
|
mListView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);
|
|
mListView.setLongClickable(true);
|
|
registerForContextMenu(mListView);
|
|
|
|
/*
|
|
* We manually save and restore the list's state because our adapter is slow.
|
|
*/
|
|
mListView.setSaveEnabled(false);
|
|
|
|
getExpandableListView().setGroupIndicator(
|
|
getResources().getDrawable(R.drawable.expander_ic_folder));
|
|
|
|
mInflater = getLayoutInflater();
|
|
|
|
Intent intent = getIntent();
|
|
mAccountId = intent.getLongExtra(EXTRA_ACCOUNT_ID, -1);
|
|
mAccount = EmailContent.Account.restoreAccountWithId(this, mAccountId);
|
|
if (mAccount == null) {
|
|
Uri uri = intent.getData();
|
|
if (uri != null) {
|
|
// TODO - DO NOT CHECK IN - add Account.restoreFromUri()
|
|
|
|
// mAccount = Preferences.getPreferences(this).getAccountByContentUri(uri);
|
|
}
|
|
}
|
|
// If no useable account was specified, just go to the accounts list screen instead
|
|
if (mAccount == null) {
|
|
AccountFolderList.actionShowAccounts(this);
|
|
finish();
|
|
return;
|
|
}
|
|
|
|
// Take the initial folder into account only if we are *not* restoring the activity already
|
|
if (savedInstanceState == null) {
|
|
mInitialFolder = intent.getStringExtra(EXTRA_INITIAL_FOLDER);
|
|
}
|
|
|
|
/*
|
|
* Since the color chip is always the same color for a given account we just cache the id
|
|
* of the chip right here.
|
|
*/
|
|
colorChipResId = colorChipResIds[(int)mAccountId % colorChipResIds.length];
|
|
|
|
/**
|
|
* "User" means the user adjusts the sync window in the UI (load more messages).
|
|
* Non-user means that it's set to a fixed window e.g. 3 days
|
|
*/
|
|
mSyncWindowUser = mAccount.getSyncWindow() == EmailContent.Account.SYNC_WINDOW_USER;
|
|
|
|
/*
|
|
mAdapter = new FolderMessageListAdapter();
|
|
|
|
final Object previousData = getLastNonConfigurationInstance();
|
|
if (previousData != null) {
|
|
//noinspection unchecked
|
|
mAdapter.mFolders = (ArrayList<FolderInfoHolder>) previousData;
|
|
}
|
|
|
|
setListAdapter(mAdapter);
|
|
|
|
if (savedInstanceState != null) {
|
|
mRestoringState = true;
|
|
onRestoreListState(savedInstanceState);
|
|
mRestoringState = false;
|
|
mRefreshRemote |= savedInstanceState.getBoolean(STATE_KEY_REFRESH_REMOTE);
|
|
}
|
|
*/
|
|
|
|
mControllerCallback = new ControllerResults();
|
|
mMessagingListener = new NewMessagingListener();
|
|
|
|
mLoadMailboxesTask = (LoadMailBoxesTask) new LoadMailBoxesTask(savedInstanceState);
|
|
mLoadMailboxesTask.execute();
|
|
|
|
setTitle(mAccount.getDescription());
|
|
}
|
|
|
|
private void onRestoreListState(Bundle savedInstanceState) {
|
|
final int expandedGroup = savedInstanceState.getInt(STATE_KEY_EXPANDED_GROUP, -1);
|
|
if (expandedGroup >= 0 && mAdapter.getGroupCount() > expandedGroup) {
|
|
mListView.expandGroup(expandedGroup);
|
|
long selectedChild = savedInstanceState.getLong(STATE_KEY_EXPANDED_GROUP_SELECTION, -1);
|
|
if (selectedChild != ExpandableListView.PACKED_POSITION_VALUE_NULL) {
|
|
mListView.setSelection(mListView.getFlatListPosition(selectedChild));
|
|
}
|
|
}
|
|
mListView.onRestoreInstanceState(savedInstanceState.getParcelable(STATE_KEY_LIST));
|
|
}
|
|
|
|
@Override
|
|
public Object onRetainNonConfigurationInstance() {
|
|
return mAdapter != null ? mAdapter.mFolders : null;
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
super.onPause();
|
|
MessagingController.getInstance(getApplication()).removeListener(mMessagingListener);
|
|
Controller.getInstance(getApplication()).removeResultCallback(mControllerCallback);
|
|
}
|
|
|
|
/**
|
|
* On resume we refresh the folder list (in the background) and we refresh the messages
|
|
* for any folder that is currently open. This guarantees that things like unread message
|
|
* count and read status are updated.
|
|
*/
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
|
|
NotificationManager notifMgr = (NotificationManager)
|
|
getSystemService(Context.NOTIFICATION_SERVICE);
|
|
notifMgr.cancel(1);
|
|
|
|
Controller.getInstance(getApplication()).addResultCallback(mControllerCallback);
|
|
MessagingController.getInstance(getApplication()).addListener(mMessagingListener);
|
|
mAccount.refresh(this);
|
|
onRefresh(false);
|
|
}
|
|
|
|
@Override
|
|
public void onSaveInstanceState(Bundle outState) {
|
|
super.onSaveInstanceState(outState);
|
|
outState.putParcelable(STATE_KEY_LIST, mListView.onSaveInstanceState());
|
|
outState.putInt(STATE_KEY_EXPANDED_GROUP, mExpandedGroup);
|
|
outState.putLong(STATE_KEY_EXPANDED_GROUP_SELECTION, mListView.getSelectedPosition());
|
|
outState.putBoolean(STATE_KEY_REFRESH_REMOTE, mRefreshRemote);
|
|
}
|
|
|
|
@Override
|
|
protected void onDestroy() {
|
|
super.onDestroy();
|
|
|
|
if (mLoadMailboxesTask != null &&
|
|
mLoadMailboxesTask.getStatus() != AsyncTask.Status.FINISHED) {
|
|
mLoadMailboxesTask.cancel(true);
|
|
mLoadMailboxesTask = null;
|
|
}
|
|
if (mLoadMessagesTask != null &&
|
|
mLoadMessagesTask.getStatus() != AsyncTask.Status.FINISHED) {
|
|
mLoadMessagesTask.cancel(true);
|
|
mLoadMessagesTask = null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onGroupCollapse(int groupPosition) {
|
|
super.onGroupCollapse(groupPosition);
|
|
mExpandedGroup = -1;
|
|
}
|
|
|
|
@Override
|
|
public void onGroupExpand(int groupPosition) {
|
|
super.onGroupExpand(groupPosition);
|
|
|
|
// This is a huge, temporary hack, since we're not really using the child cursor
|
|
// any more (we're jumping to MessageList). This is allowed here because this class
|
|
// is slated for retirement anyway.
|
|
Cursor groupCursor = mNewAdapter.getGroup(groupPosition);
|
|
long mailboxKey = groupCursor.getLong(EmailContent.Mailbox.CONTENT_ID_COLUMN);
|
|
MessageList.actionHandleAccount(this, mailboxKey, null, null);
|
|
|
|
// // We enforce viewing one folder at a time, so close the previously-opened folder
|
|
// if (mExpandedGroup != -1) {
|
|
// mListView.collapseGroup(mExpandedGroup);
|
|
// }
|
|
// mExpandedGroup = groupPosition;
|
|
//
|
|
// if (!mRestoringState) {
|
|
// /*
|
|
// * Scroll the selected item to the top of the screen.
|
|
// */
|
|
// int position = mListView.getFlatListPosition(
|
|
// ExpandableListView.getPackedPositionForGroup(groupPosition));
|
|
// mListView.setSelectionFromTop(position, 0);
|
|
// }
|
|
|
|
// // We're going to build a new cursor every time here. TODO can we cache it?
|
|
// // Kill any previous unfinished task
|
|
// if (mLoadMessagesTask != null &&
|
|
// mLoadMessagesTask.getStatus() != AsyncTask.Status.FINISHED) {
|
|
// mLoadMessagesTask.cancel(true);
|
|
// mLoadMessagesTask = null;
|
|
// }
|
|
//
|
|
// // Now start a new task to create a non-empty cursor
|
|
// Cursor groupCursor = mNewAdapter.getCursor();
|
|
// long mailboxKey = groupCursor.getLong(EmailContent.Mailbox.CONTENT_ID_COLUMN);
|
|
// mLoadMessagesTask = new LoadMessagesTask(groupPosition, mailboxKey);
|
|
// mLoadMessagesTask.execute();
|
|
|
|
|
|
// TODO replace the worker with an equivalent that syncs remote messages into folder
|
|
// final FolderInfoHolder folder = (FolderInfoHolder) mAdapter.getGroup(groupPosition);
|
|
// /*
|
|
// * We'll only do a hard refresh of a particular folder every 3 minutes or if the user
|
|
// * specifically asks for a refresh.
|
|
// */
|
|
// if (System.currentTimeMillis() - folder.lastChecked
|
|
// > UPDATE_FOLDER_ON_EXPAND_INTERVAL_MS) {
|
|
// folder.lastChecked = System.currentTimeMillis();
|
|
// // TODO: If the previous thread is already running, we should cancel it
|
|
// new Thread(new FolderUpdateWorker(folder.name, true, null, mAccount,
|
|
// MessagingController.getInstance(getApplication())))
|
|
// .start();
|
|
// }
|
|
}
|
|
|
|
@Override
|
|
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
|
|
int childPosition, long id) {
|
|
|
|
// TODO completely rewrite this. For now, quick access to new onOpenMessage call.
|
|
onOpenMessage(groupPosition, childPosition);
|
|
return true; // "handled"
|
|
|
|
/*
|
|
FolderInfoHolder folder = (FolderInfoHolder) mAdapter.getGroup(groupPosition);
|
|
if (folder.outbox) {
|
|
if (childPosition == folder.messages.size() && !folder.loading) {
|
|
mHandler.folderLoading(mAccount.getOutboxFolderName(this), true);
|
|
mHandler.progress(true);
|
|
MessagingController.getInstance(getApplication()).sendPendingMessages(
|
|
mAccount,
|
|
null);
|
|
}
|
|
return false;
|
|
}
|
|
if (childPosition == folder.messages.size() && !folder.loading) {
|
|
if (folder.status == null && mSyncWindowUser) {
|
|
MessagingController.getInstance(getApplication()).loadMoreMessages(
|
|
mAccount,
|
|
folder.name,
|
|
mMessagingListener);
|
|
return false;
|
|
}
|
|
else {
|
|
MessagingController.getInstance(getApplication()).synchronizeMailbox(
|
|
mAccount,
|
|
folder.name,
|
|
mMessagingListener);
|
|
return false;
|
|
}
|
|
}
|
|
else if (childPosition >= folder.messages.size()) {
|
|
return false;
|
|
}
|
|
MessageInfoHolder message =
|
|
(MessageInfoHolder) mAdapter.getChild(groupPosition, childPosition);
|
|
|
|
onOpenMessage(folder, message);
|
|
return true;
|
|
*/
|
|
}
|
|
|
|
private void onRefresh(final boolean forceRemote) {
|
|
if (forceRemote) {
|
|
mRefreshRemote = true;
|
|
}
|
|
|
|
if (mRefreshRemote) {
|
|
mHandler.progress(true);
|
|
Controller.getInstance(getApplication()).
|
|
updateMailboxList(mAccount, mControllerCallback);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This is typically called after refreshing the mailbox list, but it might be called at
|
|
* other times as well.
|
|
*/
|
|
private void doRefreshOpenMailbox() {
|
|
if (this.mExpandedGroup != -1) {
|
|
Cursor mailboxCursor = mNewAdapter.getGroup(mExpandedGroup);
|
|
EmailContent.Mailbox mailbox =
|
|
EmailContent.getContent(mailboxCursor, EmailContent.Mailbox.class);
|
|
|
|
if (mailbox != null) {
|
|
mHandler.progress(true);
|
|
Controller.getInstance(getApplication()).
|
|
updateMailbox(mAccount, mailbox, mControllerCallback);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback for async Controller results. This is all a placeholder until we figure out the
|
|
* final way to do this.
|
|
*/
|
|
private class ControllerResults implements Controller.Result {
|
|
public void updateMailboxListCallback(MessagingException result, long accountKey) {
|
|
mHandler.progress(false);
|
|
mHandler.listFoldersFinished();
|
|
}
|
|
|
|
public void updateMailboxCallback(MessagingException result, long accountKey,
|
|
long mailboxKey, int totalMessagesInMailbox, int numNewMessages) {
|
|
mHandler.progress(false);
|
|
}
|
|
}
|
|
|
|
@Deprecated
|
|
private void onOpenMessage(FolderInfoHolder folder, MessageInfoHolder message) {
|
|
/*
|
|
* We set read=true here for UI performance reasons. The actual value will get picked up
|
|
* on the refresh when the Activity is resumed but that may take a second or so and we
|
|
* don't want this to show and then go away.
|
|
* I've gone back and forth on this, and this gives a better UI experience, so I am
|
|
* putting it back in.
|
|
*/
|
|
if (!message.read) {
|
|
message.read = true;
|
|
mHandler.dataChanged();
|
|
}
|
|
|
|
if (folder.drafts) {
|
|
MessageCompose.actionEditDraft(this, mAccountId, message.message);
|
|
}
|
|
else {
|
|
ArrayList<String> folderUids = new ArrayList<String>();
|
|
for (MessageInfoHolder holder : folder.messages) {
|
|
folderUids.add(holder.uid);
|
|
}
|
|
MessageView.actionView(this, mAccountId, folder.name, message.uid, folderUids);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TODO we can probably do the cursor work and message ID lookup in the caller, especially
|
|
* when we implement other handlers like onReply, onDelete, etc.
|
|
*
|
|
* @param groupPosition
|
|
* @param childPosition
|
|
*/
|
|
private void onOpenMessage(int groupPosition, int childPosition) {
|
|
Cursor mailboxCursor = mNewAdapter.getGroup(groupPosition);
|
|
Cursor messageCursor = mNewAdapter.getChild(groupPosition, childPosition);
|
|
|
|
int mailboxTypeColumn = mailboxCursor.getColumnIndex(EmailContent.MailboxColumns.TYPE);
|
|
int mailboxType = mailboxCursor.getInt(mailboxTypeColumn);
|
|
if (mailboxType == EmailContent.Mailbox.TYPE_DRAFTS) {
|
|
// TODO - compose from draft
|
|
} else {
|
|
// TODO - save enough data to recreate the cursor, to enable prev/next in MessageView
|
|
int messageIdColumn = messageCursor.getColumnIndex(EmailContent.RECORD_ID);
|
|
long messageId = messageCursor.getLong(messageIdColumn);
|
|
MessageView.actionView(this, messageId);
|
|
}
|
|
|
|
}
|
|
|
|
private void onEditAccount() {
|
|
// We request a remote refresh *after* the account settings because user changes may
|
|
// change the results we get from the server. This will be picked up in onResume().
|
|
mRefreshRemote = true;
|
|
AccountSettings.actionSettings(this, mAccountId);
|
|
}
|
|
|
|
private void onAccounts() {
|
|
AccountFolderList.actionShowAccounts(this);
|
|
finish();
|
|
}
|
|
|
|
private void onCompose() {
|
|
MessageCompose.actionCompose(this, mAccountId);
|
|
}
|
|
|
|
private void onDelete(MessageInfoHolder holder) {
|
|
MessagingController.getInstance(getApplication()).deleteMessage(
|
|
mAccount,
|
|
holder.message.getFolder().getName(),
|
|
holder.message,
|
|
null);
|
|
mAdapter.removeMessage(holder.message.getFolder().getName(), holder.uid);
|
|
Toast.makeText(this, R.string.message_deleted_toast, Toast.LENGTH_SHORT).show();
|
|
}
|
|
|
|
private void onReply(MessageInfoHolder holder) {
|
|
MessageCompose.actionReply(this, mAccountId, holder.message, false);
|
|
}
|
|
|
|
private void onReplyAll(MessageInfoHolder holder) {
|
|
MessageCompose.actionReply(this, mAccountId, holder.message, true);
|
|
}
|
|
|
|
private void onForward(MessageInfoHolder holder) {
|
|
MessageCompose.actionForward(this, mAccountId, holder.message);
|
|
}
|
|
|
|
private void onToggleRead(MessageInfoHolder holder) {
|
|
MessagingController.getInstance(getApplication()).markMessageRead(
|
|
mAccount,
|
|
holder.message.getFolder().getName(),
|
|
holder.uid,
|
|
!holder.read);
|
|
holder.read = !holder.read;
|
|
onRefresh(false);
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
switch (item.getItemId()) {
|
|
case R.id.refresh:
|
|
onRefresh(true);
|
|
return true;
|
|
case R.id.accounts:
|
|
onAccounts();
|
|
return true;
|
|
case R.id.compose:
|
|
onCompose();
|
|
return true;
|
|
case R.id.account_settings:
|
|
onEditAccount();
|
|
return true;
|
|
default:
|
|
return super.onOptionsItemSelected(item);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean onCreateOptionsMenu(Menu menu) {
|
|
super.onCreateOptionsMenu(menu);
|
|
getMenuInflater().inflate(R.menu.folder_message_list_option, menu);
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean onContextItemSelected(MenuItem item) {
|
|
ExpandableListContextMenuInfo info =
|
|
(ExpandableListContextMenuInfo) item.getMenuInfo();
|
|
int groupPosition =
|
|
ExpandableListView.getPackedPositionGroup(info.packedPosition);
|
|
int childPosition =
|
|
ExpandableListView.getPackedPositionChild(info.packedPosition);
|
|
if (childPosition < mAdapter.getChildrenCount(groupPosition)) {
|
|
switch (item.getItemId()) {
|
|
case R.id.open:
|
|
onOpenMessage(groupPosition, childPosition);
|
|
break;
|
|
}
|
|
|
|
// TODO: completely rewrite this. For now, just don't crash.
|
|
/*
|
|
FolderInfoHolder folder = (FolderInfoHolder) mAdapter.getGroup(groupPosition);
|
|
MessageInfoHolder holder =
|
|
(MessageInfoHolder) mAdapter.getChild(groupPosition, childPosition);
|
|
switch (item.getItemId()) {
|
|
case R.id.open:
|
|
onOpenMessage(folder, holder);
|
|
break;
|
|
case R.id.delete:
|
|
onDelete(holder);
|
|
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.mark_as_read:
|
|
onToggleRead(holder);
|
|
break;
|
|
}
|
|
*/
|
|
}
|
|
return super.onContextItemSelected(item);
|
|
}
|
|
|
|
@Override
|
|
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
|
super.onCreateContextMenu(menu, v, menuInfo);
|
|
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuInfo;
|
|
if (ExpandableListView.getPackedPositionType(info.packedPosition) ==
|
|
ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
|
|
long packedPosition = info.packedPosition;
|
|
int groupPosition = ExpandableListView.getPackedPositionGroup(packedPosition);
|
|
int childPosition = ExpandableListView.getPackedPositionChild(packedPosition);
|
|
|
|
// TODO: completely rewrite this. For now, just don't crash.
|
|
/*
|
|
FolderInfoHolder folder = (FolderInfoHolder) mAdapter.getGroup(groupPosition);
|
|
if (folder.outbox) {
|
|
return;
|
|
}
|
|
if (childPosition < folder.messages.size()) {
|
|
getMenuInflater().inflate(R.menu.folder_message_list_context, menu);
|
|
MessageInfoHolder message =
|
|
(MessageInfoHolder) mAdapter.getChild(groupPosition, childPosition);
|
|
if (message.read) {
|
|
menu.findItem(R.id.mark_as_read).setTitle(R.string.mark_as_unread_action);
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Async task to handle the cursor query out of the UI thread
|
|
*/
|
|
private class LoadMailBoxesTask extends AsyncTask<Void, Void, Cursor> {
|
|
|
|
// We park the activity's saved instance state here, to apply
|
|
// after the cursor is loaded and the adapter is ready for UI
|
|
Bundle mSavedInstanceState;
|
|
|
|
public LoadMailBoxesTask(Bundle savedInstanceState) {
|
|
super();
|
|
mSavedInstanceState = savedInstanceState;
|
|
}
|
|
|
|
// Note that we order by the mailbox columns type column, which should be ordered
|
|
// to match desired UI presentation (e.g. INBOX at the top)
|
|
@Override
|
|
protected Cursor doInBackground(Void... params) {
|
|
// Only show boxes with mail, and sort secondarily by name
|
|
return FolderMessageList.this.managedQuery(
|
|
Mailbox.CONTENT_URI,
|
|
Mailbox.CONTENT_PROJECTION,
|
|
MailboxColumns.ACCOUNT_KEY + "=? and "
|
|
+ MailboxColumns.TYPE + '<' + Mailbox.TYPE_NOT_EMAIL,
|
|
new String[] { String.valueOf(FolderMessageList.this.mAccountId) },
|
|
MailboxColumns.TYPE + ',' + MailboxColumns.DISPLAY_NAME);
|
|
}
|
|
|
|
@Override
|
|
protected void onPostExecute(Cursor cursor) {
|
|
if (DBG_LOG_CURSORS) {
|
|
Log.d(Email.LOG_TAG, "LoadMailBoxesTask.onPostExecute: cursor length " +
|
|
String.valueOf(cursor.getCount()));
|
|
}
|
|
FolderMessageList.this.mNewAdapter =
|
|
new NewFolderMessageListAdapter(cursor, FolderMessageList.this,
|
|
mDateFormat, mTimeFormat);
|
|
mListView.setAdapter(FolderMessageList.this.mNewAdapter);
|
|
|
|
// After setting up the adapter & data, restore its state (if applicable)
|
|
if (mSavedInstanceState != null) {
|
|
mRestoringState = true;
|
|
onRestoreListState(mSavedInstanceState);
|
|
mRestoringState = false;
|
|
mRefreshRemote |= mSavedInstanceState.getBoolean(STATE_KEY_REFRESH_REMOTE);
|
|
}
|
|
|
|
// If there are no folders at all, this is probably a new account - reload from server
|
|
if (cursor.getCount() == 0) {
|
|
FolderMessageList.this.onRefresh(true);
|
|
}
|
|
|
|
/**
|
|
* For debugging purposes, add a single mailbox when there are none
|
|
*/
|
|
if (DBG_ADD_CONTENT) {
|
|
if (cursor.getCount() < 4) {
|
|
EmailContent.Mailbox box = new EmailContent.Mailbox();
|
|
|
|
box.mDisplayName = "dummy mailbox";
|
|
// box.mServerId;
|
|
// box.mParentServerId;
|
|
box.mAccountKey = FolderMessageList.this.mAccountId;
|
|
box.mType = EmailContent.Mailbox.TYPE_INBOX;
|
|
// box.mDelimiter;
|
|
// box.mSyncKey;
|
|
// box.mSyncLookback;
|
|
// box.mSyncFrequency;
|
|
// box.mSyncTime;
|
|
box.mUnreadCount = 100;
|
|
box.mFlagVisible = true;
|
|
// box.mFlags;
|
|
// box.mVisibleLimit;
|
|
box.save(FolderMessageList.this);
|
|
long mailbox1Id = box.mId;
|
|
|
|
box = new EmailContent.Mailbox();
|
|
|
|
box.mDisplayName = "dummy mailbox 2";
|
|
box.mAccountKey = FolderMessageList.this.mAccountId;
|
|
box.mType = EmailContent.Mailbox.TYPE_MAIL;
|
|
box.mUnreadCount = 75;
|
|
box.mFlagVisible = true;
|
|
box.save(FolderMessageList.this);
|
|
long mailbox2Id = box.mId;
|
|
|
|
box = new EmailContent.Mailbox();
|
|
|
|
box.mDisplayName = "dummy mailbox 3";
|
|
box.mAccountKey = FolderMessageList.this.mAccountId;
|
|
box.mType = EmailContent.Mailbox.TYPE_MAIL;
|
|
box.mUnreadCount = 75;
|
|
box.mFlagVisible = true;
|
|
box.save(FolderMessageList.this);
|
|
long mailbox3Id = box.mId;
|
|
|
|
box = new EmailContent.Mailbox();
|
|
|
|
box.mDisplayName = "dummy mailbox diff acct";
|
|
box.mAccountKey = FolderMessageList.this.mAccountId + 1;
|
|
box.mType = EmailContent.Mailbox.TYPE_INBOX;
|
|
box.mUnreadCount = 50;
|
|
box.mFlagVisible = true;
|
|
box.save(FolderMessageList.this);
|
|
|
|
// finally toss in a few messages
|
|
|
|
EmailContent.Message msg = new EmailContent.Message();
|
|
msg.mDisplayName = "sender1@google.com";
|
|
msg.mTimeStamp = 1010101090; // invent a better timestamp
|
|
msg.mSubject = "message 1";
|
|
// msg.mPreview;
|
|
// msg.mFlagRead = false;
|
|
// msg.mFlagLoaded = 0;
|
|
// msg.mFlagFavorite = false;
|
|
// msg.mFlagAttachment = false;
|
|
// msg.mFlags = 0;
|
|
// msg.mText = "This is the body text of the 1st email.";
|
|
// msg.mTextInfo = "X;X;8;" + msg.mText.length()*2;
|
|
// msg.mHtmlInfo;
|
|
// msg.mServerId;
|
|
// msg.mServerIntId;
|
|
// msg.mClientId;
|
|
// msg.mMessageId;
|
|
// msg.mThreadId;
|
|
// msg.mBodyKey;
|
|
msg.mMailboxKey = mailbox1Id;
|
|
msg.mAccountKey = FolderMessageList.this.mAccountId;
|
|
// msg.mReferenceKey;
|
|
// msg.mSender;
|
|
msg.mFrom = "sender1@google.com";
|
|
// msg.mTo;
|
|
// msg.mCc;
|
|
// msg.mBcc;
|
|
// msg.mReplyTo;
|
|
msg.save(FolderMessageList.this);
|
|
|
|
msg = new EmailContent.Message();
|
|
msg.mDisplayName = "sender2@google.com";
|
|
msg.mTimeStamp = 1010101091; // invent a better timestamp
|
|
msg.mSubject = "message 2";
|
|
msg.mMailboxKey = mailbox1Id;
|
|
msg.mAccountKey = FolderMessageList.this.mAccountId;
|
|
msg.mFrom = "sender2@google.com";
|
|
// msg.mText = "This is the body text of this email.";
|
|
// msg.mTextInfo = "X;X;8;" + msg.mText.length()*2;
|
|
msg.save(FolderMessageList.this);
|
|
|
|
msg = new EmailContent.Message();
|
|
msg.mDisplayName = "sender3@google.com";
|
|
msg.mTimeStamp = 1010101092; // invent a better timestamp
|
|
msg.mSubject = "message 3";
|
|
msg.mMailboxKey = mailbox1Id;
|
|
msg.mAccountKey = FolderMessageList.this.mAccountId;
|
|
msg.mFrom = "sender3@google.com";
|
|
// msg.mText = "This is the body text of the 3rd email.";
|
|
// msg.mTextInfo = "X;X;8;" + msg.mText.length()*2;
|
|
msg.save(FolderMessageList.this);
|
|
|
|
msg = new EmailContent.Message();
|
|
msg.mDisplayName = "sender4@google.com";
|
|
msg.mTimeStamp = 1010101093; // invent a better timestamp
|
|
msg.mSubject = "message 4";
|
|
msg.mMailboxKey = mailbox2Id;
|
|
msg.mAccountKey = FolderMessageList.this.mAccountId;
|
|
msg.mFrom = "sender4@google.com";
|
|
// msg.mText = "This is the body text of the 4th email.";
|
|
// msg.mTextInfo = "X;X;8;" + msg.mText.length()*2;
|
|
msg.save(FolderMessageList.this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Async task for loading a single folder out of the UI thread
|
|
*/
|
|
private class LoadMessagesTask extends AsyncTask<Void, Void, Cursor> {
|
|
|
|
private int mGroupNumber;
|
|
private long mMailboxKey;
|
|
|
|
/**
|
|
* Special constructor to cache some local info
|
|
*/
|
|
public LoadMessagesTask(int groupNumber, long mailboxKey) {
|
|
mGroupNumber = groupNumber;
|
|
mMailboxKey = mailboxKey;
|
|
}
|
|
|
|
// TODO should use a lighter-weight projection of messages because we're only
|
|
// displaying summaries, not the whole message.
|
|
@Override
|
|
protected Cursor doInBackground(Void... params) {
|
|
return FolderMessageList.this.managedQuery(
|
|
EmailContent.Message.CONTENT_URI,
|
|
EmailContent.Message.CONTENT_PROJECTION,
|
|
EmailContent.MessageColumns.ACCOUNT_KEY + "=?" +
|
|
" AND " + EmailContent.MessageColumns.MAILBOX_KEY + "=?",
|
|
new String[] {
|
|
String.valueOf(FolderMessageList.this.mAccountId),
|
|
String.valueOf(mMailboxKey)
|
|
},
|
|
EmailContent.MessageColumns.TIMESTAMP + " DESC");
|
|
}
|
|
|
|
@Override
|
|
protected void onPostExecute(Cursor cursor) {
|
|
if (DBG_LOG_CURSORS) {
|
|
Log.d(Email.LOG_TAG, "LoadMessagesTask.onPostExecute: cursor length " +
|
|
String.valueOf(cursor.getCount()));
|
|
}
|
|
// Confirm that we're still stuffing the correct group #
|
|
if (mGroupNumber == FolderMessageList.this.mExpandedGroup) {
|
|
FolderMessageList.this.mNewAdapter.setChildrenCursor(mGroupNumber, cursor);
|
|
|
|
// TODO implement 3-minute refresh rule, instead of "every time"
|
|
doRefreshOpenMailbox();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class implements the tree for displaying folders and messages based on cursors.
|
|
*
|
|
* TODO: There is a bug # 1913302 describing a bug in ResourceCursorTreeAdapter, mixing up
|
|
* recycled views when you have "last child" or "expanded/contracted group" layouts. Because of
|
|
* this, we're going to simplify (for now) and simply display the message list, without the
|
|
* added "last" view of the loading status. This is all placeholder code until we move folder
|
|
* view into the Account screen.
|
|
*/
|
|
private static class NewFolderMessageListAdapter extends CursorTreeAdapter {
|
|
|
|
Context mContext;
|
|
private LayoutInflater mInflater;
|
|
|
|
int mFolderNameColumn;
|
|
int mFolderUnreadCountColumn;
|
|
|
|
boolean haveChildColumns;
|
|
int mChildReadFlagColumn;
|
|
int mChildDisplayNameColumn;
|
|
int mChildDateColumn;
|
|
int mChildSubjectColumn;
|
|
|
|
private java.text.DateFormat mDateFormat;
|
|
private java.text.DateFormat mTimeFormat;
|
|
|
|
public NewFolderMessageListAdapter(Cursor cursor, Context context, DateFormat dateFormat,
|
|
DateFormat timeFormat) {
|
|
super(cursor, context);
|
|
mContext = context;
|
|
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
|
mDateFormat = dateFormat;
|
|
mTimeFormat = timeFormat;
|
|
|
|
mFolderNameColumn =
|
|
cursor.getColumnIndexOrThrow(EmailContent.MailboxColumns.DISPLAY_NAME);
|
|
mFolderUnreadCountColumn =
|
|
cursor.getColumnIndexOrThrow(EmailContent.MailboxColumns.UNREAD_COUNT);
|
|
}
|
|
|
|
/**
|
|
* We return null here (no immediate cursor availability) and use an AsyncTask to get
|
|
* the cursor in certain onclick situations
|
|
*/
|
|
@Override
|
|
protected Cursor getChildrenCursor(Cursor groupCursor) {
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Overriding this allows the child cursors to be requeried on dataset changes
|
|
*/
|
|
@Override
|
|
public void notifyDataSetChanged() {
|
|
notifyDataSetChanged(false);
|
|
}
|
|
|
|
@Override
|
|
protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild)
|
|
{
|
|
if (!haveChildColumns) {
|
|
mChildReadFlagColumn =
|
|
cursor.getColumnIndexOrThrow(EmailContent.MessageColumns.FLAG_READ);
|
|
mChildDisplayNameColumn =
|
|
cursor.getColumnIndexOrThrow(EmailContent.MessageColumns.DISPLAY_NAME);
|
|
mChildDateColumn =
|
|
cursor.getColumnIndexOrThrow(EmailContent.MessageColumns.TIMESTAMP);
|
|
mChildSubjectColumn =
|
|
cursor.getColumnIndexOrThrow(EmailContent.MessageColumns.SUBJECT);
|
|
haveChildColumns = true;
|
|
}
|
|
for (int i = 0; i < sChildToIds.length; i++) {
|
|
View v = view.findViewById(sChildToIds[i]);
|
|
String text = null;
|
|
switch(v.getId()) {
|
|
case R.id.chip:
|
|
// TODO - change chip color based on acct#
|
|
boolean readFlag = cursor.getInt(mChildReadFlagColumn) != 0;
|
|
v.getBackground().setAlpha(readFlag ? 0 : 255);
|
|
break;
|
|
case R.id.from:
|
|
text = cursor.getString(mChildDisplayNameColumn);
|
|
break;
|
|
case R.id.date:
|
|
long timestamp = cursor.getLong(mChildDateColumn);
|
|
Date date = new Date(timestamp);
|
|
if (Utility.isDateToday(date)) {
|
|
text = mTimeFormat.format(date);
|
|
} else {
|
|
text = mDateFormat.format(date);
|
|
}
|
|
break;
|
|
case R.id.subject:
|
|
// TODO maybe start using snippet instead of subject
|
|
text = cursor.getString(mChildSubjectColumn);
|
|
break;
|
|
}
|
|
if (text != null) ((TextView)v).setText(text);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded)
|
|
{
|
|
for (int i = 0; i < sGroupToIds.length; i++) {
|
|
View v = view.findViewById(sGroupToIds[i]);
|
|
String text = null;
|
|
switch(v.getId()) {
|
|
case R.id.folder_name:
|
|
text = cursor.getString(mFolderNameColumn);
|
|
break;
|
|
case R.id.folder_status:
|
|
// TODO - display status, if any
|
|
v.setVisibility(View.GONE);
|
|
break;
|
|
case R.id.new_message_count:
|
|
text = cursor.getString(mFolderUnreadCountColumn);
|
|
break;
|
|
}
|
|
if (text != null) ((TextView)v).setText(text);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected View newChildView(Context context, Cursor cursor, boolean isLastChild,
|
|
ViewGroup parent) {
|
|
return mInflater.inflate(R.layout.folder_message_list_child, parent, false);
|
|
}
|
|
|
|
@Override
|
|
protected View newGroupView(Context context, Cursor cursor, boolean isExpanded,
|
|
ViewGroup parent) {
|
|
return mInflater.inflate(R.layout.folder_message_list_group, parent, false);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* This class replaces the old MessagingListener, and most of it is not implemented because
|
|
* we're switching over to cursors for most notifications.
|
|
*/
|
|
private class NewMessagingListener extends MessagingListener {
|
|
}
|
|
|
|
class FolderMessageListAdapter extends BaseExpandableListAdapter {
|
|
private ArrayList<FolderInfoHolder> mFolders = new ArrayList<FolderInfoHolder>();
|
|
|
|
private MessagingListener mListener = new MessagingListener() {
|
|
@Override
|
|
public void listFoldersStarted(EmailContent.Account account) {
|
|
if (!account.equals(mAccount)) {
|
|
return;
|
|
}
|
|
mHandler.progress(true);
|
|
}
|
|
|
|
@Override
|
|
public void listFoldersFailed(EmailContent.Account account, String message) {
|
|
if (!account.equals(mAccount)) {
|
|
return;
|
|
}
|
|
mHandler.progress(false);
|
|
if (Config.LOGV) {
|
|
Log.v(Email.LOG_TAG, "listFoldersFailed " + message);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void listFoldersFinished(EmailContent.Account account) {
|
|
if (!account.equals(mAccount)) {
|
|
return;
|
|
}
|
|
mHandler.progress(false);
|
|
mHandler.listFoldersFinished();
|
|
}
|
|
|
|
/*
|
|
@Override
|
|
public void listLocalMessagesStarted(EmailContent.Account account, String folder) {
|
|
if (!account.equals(mAccount)) {
|
|
return;
|
|
}
|
|
mHandler.progress(true);
|
|
mHandler.folderLoading(folder, true);
|
|
}
|
|
|
|
@Override
|
|
public void listLocalMessagesFailed(EmailContent.Account account, String folder,
|
|
String message) {
|
|
if (!account.equals(mAccount)) {
|
|
return;
|
|
}
|
|
mHandler.progress(false);
|
|
mHandler.folderLoading(folder, false);
|
|
}
|
|
|
|
@Override
|
|
public void listLocalMessagesFinished(EmailContent.Account account, String folder) {
|
|
if (!account.equals(mAccount)) {
|
|
return;
|
|
}
|
|
mHandler.progress(false);
|
|
mHandler.folderLoading(folder, false);
|
|
}
|
|
|
|
@Override
|
|
public void listLocalMessages(EmailContent.Account account, String folder,
|
|
Message[] messages) {
|
|
if (!account.equals(mAccount)) {
|
|
return;
|
|
}
|
|
mHandler.synchronizeMessages(folder, messages);
|
|
}
|
|
*/
|
|
@Override
|
|
public void synchronizeMailboxStarted(EmailContent.Account account,
|
|
EmailContent.Mailbox folder) {
|
|
if (!account.equals(mAccount)) {
|
|
return;
|
|
}
|
|
mHandler.progress(true);
|
|
// mHandler.folderLoading(folder, true);
|
|
// mHandler.folderStatus(folder, null, false);
|
|
}
|
|
|
|
@Override
|
|
public void synchronizeMailboxFinished(EmailContent.Account account,
|
|
EmailContent.Mailbox folder, int totalMessagesInMailbox, int numNewMessages) {
|
|
if (!account.equals(mAccount)) {
|
|
return;
|
|
}
|
|
mHandler.progress(false);
|
|
// mHandler.folderLoading(folder, false);
|
|
// mHandler.folderStatus(folder, null, false);
|
|
onRefresh(false);
|
|
}
|
|
|
|
@Override
|
|
public void synchronizeMailboxFailed(EmailContent.Account account,
|
|
EmailContent.Mailbox folder, Exception e) {
|
|
if (!account.equals(mAccount)) {
|
|
return;
|
|
}
|
|
mHandler.progress(false);
|
|
// mHandler.folderLoading(folder, false);
|
|
// Use exception details to select a decent string
|
|
// TODO combine with very similar code in AccountSettingsCheckSetup
|
|
int id = R.string.status_network_error;
|
|
if (e instanceof AuthenticationFailedException) {
|
|
id = R.string.account_setup_failed_dlg_auth_message;
|
|
} else if (e instanceof CertificateValidationException) {
|
|
id = R.string.account_setup_failed_dlg_certificate_message;
|
|
} else if (e instanceof MessagingException) {
|
|
MessagingException me = (MessagingException) e;
|
|
switch (me.getExceptionType()) {
|
|
case MessagingException.IOERROR:
|
|
id = R.string.account_setup_failed_ioerror;
|
|
break;
|
|
case MessagingException.TLS_REQUIRED:
|
|
id = R.string.account_setup_failed_tls_required;
|
|
break;
|
|
case MessagingException.AUTH_REQUIRED:
|
|
id = R.string.account_setup_failed_auth_required;
|
|
break;
|
|
case MessagingException.GENERAL_SECURITY:
|
|
id = R.string.account_setup_failed_security;
|
|
break;
|
|
}
|
|
}
|
|
// mHandler.folderStatus(folder, getString(id), true);
|
|
}
|
|
|
|
@Override
|
|
public void emptyTrashCompleted(EmailContent.Account account) {
|
|
if (!account.equals(mAccount)) {
|
|
return;
|
|
}
|
|
onRefresh(false);
|
|
}
|
|
|
|
@Override
|
|
public void sendPendingMessagesCompleted(EmailContent.Account account) {
|
|
if (!account.equals(mAccount)) {
|
|
return;
|
|
}
|
|
mHandler.folderLoading(account.getOutboxFolderName(FolderMessageList.this), false);
|
|
mHandler.progress(false);
|
|
onRefresh(false);
|
|
}
|
|
|
|
@Override
|
|
public void sendPendingMessagesFailed(EmailContent.Account account, Exception reason) {
|
|
if (!account.equals(mAccount)) {
|
|
return;
|
|
}
|
|
String outboxName = account.getOutboxFolderName(FolderMessageList.this);
|
|
mHandler.folderLoading(outboxName, false);
|
|
mHandler.progress(false);
|
|
mHandler.folderStatus(outboxName, reason.getMessage(), false);
|
|
onRefresh(false);
|
|
}
|
|
|
|
@Override
|
|
public void sendPendingMessageFailed(EmailContent.Account account, Message message,
|
|
Exception reason) {
|
|
if (!account.equals(mAccount)) {
|
|
return;
|
|
}
|
|
// TODO May we move failed message to draft folder?
|
|
//mHandler.removeMessageByUid(folder, message.getUid());
|
|
//mHandler.folderStatus(account.getOutboxFolderName(), reason.getMessage(), false);
|
|
}
|
|
|
|
@Override
|
|
public void messageUidChanged(EmailContent.Account account, String folder,
|
|
String oldUid, String newUid) {
|
|
if (!mAccount.equals(account)) {
|
|
return;
|
|
}
|
|
mHandler.messageUidChanged(folder, oldUid, newUid);
|
|
}
|
|
};
|
|
|
|
private Drawable mAttachmentIcon;
|
|
|
|
FolderMessageListAdapter() {
|
|
mAttachmentIcon = getResources().getDrawable(R.drawable.ic_mms_attachment_small);
|
|
}
|
|
|
|
/*
|
|
* Set special folder boolean indicator and display name in FolderInfoHolder
|
|
*/
|
|
private void setSpecialFolderInfo(FolderInfoHolder holder) {
|
|
String folderName = holder.name;
|
|
holder.special = true;
|
|
if (folderName.equalsIgnoreCase(Email.INBOX)) {
|
|
holder.inbox = true;
|
|
holder.displayName = getString(R.string.special_mailbox_name_inbox);
|
|
} else if (folderName.equals(mAccount.getDraftsFolderName(FolderMessageList.this))) {
|
|
holder.drafts = true;
|
|
holder.displayName = getString(R.string.special_mailbox_display_name_drafts);
|
|
} else if (folderName.equals(mAccount.getOutboxFolderName(FolderMessageList.this))) {
|
|
holder.outbox = true;
|
|
holder.displayName = getString(R.string.special_mailbox_display_name_outbox);
|
|
} else if (folderName.equals(mAccount.getSentFolderName(FolderMessageList.this))) {
|
|
holder.sent = true;
|
|
holder.displayName = getString(R.string.special_mailbox_display_name_sent);
|
|
} else if (folderName.equals(mAccount.getTrashFolderName(FolderMessageList.this))) {
|
|
holder.trash = true;
|
|
holder.displayName = getString(R.string.special_mailbox_display_name_trash);
|
|
} else {
|
|
holder.special = false;
|
|
holder.displayName = folderName;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This code is invoked (in the UI thread) when listFoldersFinished() happens. It
|
|
* implements the UI policy of opening the requested folder (if any).
|
|
*/
|
|
public void doListFoldersFinished() {
|
|
if (mInitialFolder != null) {
|
|
int groupPosition = getFolderPosition(mInitialFolder);
|
|
mInitialFolder = null;
|
|
if (groupPosition != -1) {
|
|
mListView.expandGroup(groupPosition);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void removeMessage(String folder, String messageUid) {
|
|
FolderInfoHolder f = getFolder(folder);
|
|
if (f == null) {
|
|
return;
|
|
}
|
|
MessageInfoHolder m = getMessage(f, messageUid);
|
|
if (m == null) {
|
|
return;
|
|
}
|
|
mHandler.removeMessage(f, m);
|
|
}
|
|
|
|
public void addOrUpdateMessage(String folder, Message message) {
|
|
addOrUpdateMessage(folder, message, true, true);
|
|
}
|
|
|
|
private void addOrUpdateMessage(FolderInfoHolder folder, Message message,
|
|
boolean sort, boolean notify) {
|
|
MessageInfoHolder m = getMessage(folder, message.getUid());
|
|
if (m == null) {
|
|
m = new MessageInfoHolder(message, folder, mDateFormat, mTimeFormat);
|
|
folder.messages.add(m);
|
|
}
|
|
else {
|
|
m.populate(message, folder);
|
|
}
|
|
if (sort) {
|
|
Collections.sort(folder.messages);
|
|
}
|
|
if (notify) {
|
|
mHandler.dataChanged();
|
|
}
|
|
}
|
|
|
|
private void addOrUpdateMessage(String folder, Message message,
|
|
boolean sort, boolean notify) {
|
|
FolderInfoHolder f = getFolder(folder);
|
|
if (f == null) {
|
|
return;
|
|
}
|
|
addOrUpdateMessage(f, message, sort, notify);
|
|
}
|
|
|
|
public MessageInfoHolder getMessage(FolderInfoHolder folder, String messageUid) {
|
|
for (MessageInfoHolder message : folder.messages) {
|
|
if (message.uid.equals(messageUid)) {
|
|
return message;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public int getGroupCount() {
|
|
return mFolders.size();
|
|
}
|
|
|
|
public long getGroupId(int groupPosition) {
|
|
return groupPosition;
|
|
}
|
|
|
|
public Object getGroup(int groupPosition) {
|
|
return mFolders.get(groupPosition);
|
|
}
|
|
|
|
public FolderInfoHolder getFolder(String folder) {
|
|
FolderInfoHolder folderHolder = null;
|
|
for (int i = 0, count = getGroupCount(); i < count; i++) {
|
|
FolderInfoHolder holder = (FolderInfoHolder) getGroup(i);
|
|
if (holder.name.equals(folder)) {
|
|
folderHolder = holder;
|
|
}
|
|
}
|
|
return folderHolder;
|
|
}
|
|
|
|
/**
|
|
* Gets the group position of the given folder or returns -1 if the folder is not
|
|
* found.
|
|
* @param folder
|
|
* @return
|
|
*/
|
|
public int getFolderPosition(String folder) {
|
|
for (int i = 0, count = getGroupCount(); i < count; i++) {
|
|
FolderInfoHolder holder = (FolderInfoHolder) getGroup(i);
|
|
if (holder.name.equals(folder)) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
|
|
ViewGroup parent) {
|
|
FolderInfoHolder folder = (FolderInfoHolder) getGroup(groupPosition);
|
|
View view;
|
|
if (convertView != null) {
|
|
view = convertView;
|
|
} else {
|
|
view = mInflater.inflate(R.layout.folder_message_list_group, parent, false);
|
|
}
|
|
FolderViewHolder holder = (FolderViewHolder) view.getTag();
|
|
if (holder == null) {
|
|
holder = new FolderViewHolder();
|
|
holder.folderName = (TextView) view.findViewById(R.id.folder_name);
|
|
holder.newMessageCount = (TextView) view.findViewById(R.id.new_message_count);
|
|
holder.folderStatus = (TextView) view.findViewById(R.id.folder_status);
|
|
view.setTag(holder);
|
|
}
|
|
holder.folderName.setText(folder.displayName);
|
|
|
|
if (folder.status == null) {
|
|
holder.folderStatus.setVisibility(View.GONE);
|
|
}
|
|
else {
|
|
holder.folderStatus.setText(folder.status);
|
|
holder.folderStatus.setVisibility(View.VISIBLE);
|
|
}
|
|
|
|
if ((folder.inbox || !folder.special) && folder.unreadMessageCount != 0) {
|
|
holder.newMessageCount.setText(Integer.toString(folder.unreadMessageCount));
|
|
holder.newMessageCount.setVisibility(View.VISIBLE);
|
|
}
|
|
else if ((folder.outbox || folder.drafts) && folder.messageCount > 0) {
|
|
holder.newMessageCount.setText(Integer.toString(folder.messageCount));
|
|
holder.newMessageCount.setVisibility(View.VISIBLE);
|
|
}
|
|
else {
|
|
holder.newMessageCount.setVisibility(View.GONE);
|
|
}
|
|
return view;
|
|
}
|
|
|
|
public int getChildrenCount(int groupPosition) {
|
|
FolderInfoHolder folder = (FolderInfoHolder) getGroup(groupPosition);
|
|
return folder.messages.size() + 1;
|
|
}
|
|
|
|
public long getChildId(int groupPosition, int childPosition) {
|
|
FolderInfoHolder folder = (FolderInfoHolder) getGroup(groupPosition);
|
|
if (childPosition < folder.messages.size()) {
|
|
MessageInfoHolder holder = folder.messages.get(childPosition);
|
|
return ((LocalStore.LocalMessage) holder.message).getId();
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
public Object getChild(int groupPosition, int childPosition) {
|
|
FolderInfoHolder folder = (FolderInfoHolder) getGroup(groupPosition);
|
|
return folder.messages.get(childPosition);
|
|
}
|
|
|
|
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
|
|
View convertView, ViewGroup parent) {
|
|
FolderInfoHolder folder = (FolderInfoHolder) getGroup(groupPosition);
|
|
if (isLastChild) {
|
|
View view;
|
|
if ((convertView != null)
|
|
&& (convertView.getId()
|
|
== R.layout.folder_message_list_child_footer)) {
|
|
view = convertView;
|
|
}
|
|
else {
|
|
view = mInflater.inflate(R.layout.folder_message_list_child_footer,
|
|
parent, false);
|
|
view.setId(R.layout.folder_message_list_child_footer);
|
|
}
|
|
FooterViewHolder holder = (FooterViewHolder) view.getTag();
|
|
if (holder == null) {
|
|
holder = new FooterViewHolder();
|
|
holder.progress = (ProgressBar) view.findViewById(R.id.progress);
|
|
holder.main = (TextView) view.findViewById(R.id.main_text);
|
|
view.setTag(holder);
|
|
}
|
|
if (folder.loading) {
|
|
if (folder.outbox) {
|
|
holder.main.setText(R.string.status_sending_messages);
|
|
} else {
|
|
holder.main.setText(R.string.status_loading_more);
|
|
}
|
|
holder.progress.setVisibility(View.VISIBLE);
|
|
}
|
|
else {
|
|
if (folder.status == null) {
|
|
if (folder.outbox) {
|
|
holder.main.setText(R.string.message_list_send_pending_messages_action);
|
|
} else {
|
|
if (mSyncWindowUser) {
|
|
holder.main.setText(
|
|
R.string.message_list_load_more_messages_action);
|
|
} else {
|
|
holder.main.setText(R.string.refresh_action);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (folder.outbox) {
|
|
holder.main.setText(R.string.status_sending_messages_failed);
|
|
} else {
|
|
holder.main.setText(R.string.status_loading_more_failed);
|
|
}
|
|
}
|
|
holder.progress.setVisibility(View.GONE);
|
|
}
|
|
return view;
|
|
}
|
|
else {
|
|
MessageInfoHolder message =
|
|
(MessageInfoHolder) getChild(groupPosition, childPosition);
|
|
View view;
|
|
if ((convertView != null)
|
|
&& (convertView.getId() != R.layout.folder_message_list_child_footer)) {
|
|
view = convertView;
|
|
} else {
|
|
view = mInflater.inflate(R.layout.folder_message_list_child, parent, false);
|
|
}
|
|
MessageViewHolder holder = (MessageViewHolder) view.getTag();
|
|
if (holder == null) {
|
|
holder = new MessageViewHolder();
|
|
holder.subject = (TextView) view.findViewById(R.id.subject);
|
|
holder.from = (TextView) view.findViewById(R.id.from);
|
|
holder.date = (TextView) view.findViewById(R.id.date);
|
|
holder.chip = view.findViewById(R.id.chip);
|
|
/*
|
|
* TODO
|
|
* The line below and the commented lines a bit further down are work
|
|
* in progress for outbox status. They should not be removed.
|
|
*/
|
|
// holder.status = (TextView) view.findViewById(R.id.status);
|
|
|
|
/*
|
|
* This will need to move to below if we ever convert this whole thing
|
|
* to a combined inbox.
|
|
*/
|
|
holder.chip.setBackgroundResource(colorChipResId);
|
|
|
|
view.setTag(holder);
|
|
}
|
|
holder.chip.getBackground().setAlpha(message.read ? 0 : 255);
|
|
holder.subject.setText(message.subject);
|
|
holder.subject.setTypeface(null, message.read ? Typeface.NORMAL : Typeface.BOLD);
|
|
holder.from.setText(message.sender);
|
|
holder.from.setTypeface(null, message.read ? Typeface.NORMAL : Typeface.BOLD);
|
|
holder.date.setText(message.date);
|
|
holder.from.setCompoundDrawablesWithIntrinsicBounds(null, null,
|
|
message.hasAttachments ? mAttachmentIcon : null, null);
|
|
// if (folder.outbox) {
|
|
// holder.status.setText("Sending");
|
|
// }
|
|
// else {
|
|
// holder.status.setText("");
|
|
// }
|
|
return view;
|
|
}
|
|
}
|
|
|
|
public boolean hasStableIds() {
|
|
return true;
|
|
}
|
|
|
|
public boolean isChildSelectable(int groupPosition, int childPosition) {
|
|
return childPosition < getChildrenCount(groupPosition);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Holder for a single folder's information.
|
|
*/
|
|
static class FolderInfoHolder implements Comparable<FolderInfoHolder> {
|
|
public String name;
|
|
public String displayName;
|
|
public ArrayList<MessageInfoHolder> messages;
|
|
public long lastChecked;
|
|
public int messageCount;
|
|
public int unreadMessageCount;
|
|
public boolean loading;
|
|
public String status;
|
|
public boolean lastCheckFailed;
|
|
|
|
/**
|
|
* Special folder indicator.
|
|
*/
|
|
public boolean special; // One of the following five folders.
|
|
public boolean inbox;
|
|
public boolean drafts;
|
|
public boolean outbox;
|
|
public boolean sent;
|
|
public boolean trash;
|
|
|
|
public int compareTo(FolderInfoHolder o) {
|
|
String s1 = this.name;
|
|
String s2 = o.name;
|
|
if (Email.INBOX.equalsIgnoreCase(s1)) {
|
|
return -1;
|
|
} else if (Email.INBOX.equalsIgnoreCase(s2)) {
|
|
return 1;
|
|
} else
|
|
return s1.toUpperCase().compareTo(s2.toUpperCase());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Holder for a single message's information.
|
|
*/
|
|
static class MessageInfoHolder implements Comparable<MessageInfoHolder> {
|
|
public String subject;
|
|
public String date;
|
|
public Date compareDate;
|
|
public String sender;
|
|
public boolean hasAttachments;
|
|
public String uid;
|
|
public boolean read;
|
|
public Message message;
|
|
|
|
private java.text.DateFormat mDateFormat;
|
|
private java.text.DateFormat mTimeFormat;
|
|
|
|
public MessageInfoHolder(Message m, FolderInfoHolder folder, DateFormat formatForDate,
|
|
DateFormat formatForTime) {
|
|
mDateFormat = formatForDate;
|
|
mTimeFormat = formatForTime;
|
|
populate(m, folder);
|
|
}
|
|
|
|
public void populate(Message m, FolderInfoHolder folder) {
|
|
try {
|
|
LocalMessage message = (LocalMessage) m;
|
|
Date date = message.getSentDate();
|
|
this.compareDate = date;
|
|
if (Utility.isDateToday(date)) {
|
|
this.date = mTimeFormat.format(date);
|
|
}
|
|
else {
|
|
this.date = mDateFormat.format(date);
|
|
}
|
|
this.hasAttachments = message.getAttachmentCount() > 0;
|
|
this.read = message.isSet(Flag.SEEN);
|
|
if (folder.outbox || folder.drafts || folder.sent) {
|
|
this.sender = Address.toFriendly(
|
|
message.getRecipients(RecipientType.TO));
|
|
}
|
|
else {
|
|
this.sender = Address.toFriendly(message.getFrom());
|
|
}
|
|
this.subject = message.getSubject();
|
|
this.uid = message.getUid();
|
|
this.message = m;
|
|
}
|
|
catch (MessagingException me) {
|
|
if (Config.LOGV) {
|
|
Log.v(Email.LOG_TAG, "Unable to load message info", me);
|
|
}
|
|
}
|
|
}
|
|
|
|
public int compareTo(MessageInfoHolder o) {
|
|
return this.compareDate.compareTo(o.compareDate) * -1;
|
|
}
|
|
}
|
|
|
|
static class FolderViewHolder {
|
|
public TextView folderName;
|
|
public TextView folderStatus;
|
|
public TextView newMessageCount;
|
|
}
|
|
|
|
static class MessageViewHolder {
|
|
public TextView subject;
|
|
public TextView preview;
|
|
public TextView from;
|
|
public TextView date;
|
|
public View chip;
|
|
}
|
|
|
|
static class FooterViewHolder {
|
|
public ProgressBar progress;
|
|
public TextView main;
|
|
}
|
|
|
|
|
|
}
|