917 lines
35 KiB
Java
917 lines
35 KiB
Java
/*
|
|
* Copyright (C) 2009 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.R;
|
|
import com.android.email.Utility;
|
|
import com.android.email.activity.setup.AccountSettings;
|
|
import com.android.email.activity.setup.AccountSetupBasics;
|
|
import com.android.email.mail.MessagingException;
|
|
import com.android.email.mail.Store;
|
|
import com.android.email.provider.EmailContent;
|
|
import com.android.email.provider.EmailContent.Account;
|
|
import com.android.email.provider.EmailContent.Mailbox;
|
|
import com.android.email.provider.EmailContent.MailboxColumns;
|
|
import com.android.email.provider.EmailContent.Message;
|
|
import com.android.email.provider.EmailContent.MessageColumns;
|
|
|
|
import android.app.AlertDialog;
|
|
import android.app.Dialog;
|
|
import android.app.ListActivity;
|
|
import android.app.NotificationManager;
|
|
import android.content.ContentUris;
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.content.Intent;
|
|
import android.database.Cursor;
|
|
import android.database.MatrixCursor;
|
|
import android.database.MergeCursor;
|
|
import android.database.MatrixCursor.RowBuilder;
|
|
import android.net.Uri;
|
|
import android.os.AsyncTask;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.view.ContextMenu;
|
|
import android.view.KeyEvent;
|
|
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.view.View.OnClickListener;
|
|
import android.widget.AdapterView;
|
|
import android.widget.CursorAdapter;
|
|
import android.widget.ImageView;
|
|
import android.widget.ListView;
|
|
import android.widget.ProgressBar;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
import android.widget.AdapterView.OnItemClickListener;
|
|
|
|
public class AccountFolderList extends ListActivity
|
|
implements OnItemClickListener, OnClickListener {
|
|
private static final int DIALOG_REMOVE_ACCOUNT = 1;
|
|
/**
|
|
* Key codes used to open a debug settings screen.
|
|
*/
|
|
private static int[] secretKeyCodes = {
|
|
KeyEvent.KEYCODE_D, KeyEvent.KEYCODE_E, KeyEvent.KEYCODE_B, KeyEvent.KEYCODE_U,
|
|
KeyEvent.KEYCODE_G
|
|
};
|
|
private int mSecretKeyCodeIndex = 0;
|
|
|
|
private static final String ICICLE_SELECTED_ACCOUNT = "com.android.email.selectedAccount";
|
|
private EmailContent.Account mSelectedContextAccount;
|
|
|
|
private ListView mListView;
|
|
private ProgressBar mProgressIcon;
|
|
|
|
AccountsAdapter mListAdapter;
|
|
|
|
LoadAccountsTask mLoadAccountsTask;
|
|
|
|
private MessageListHandler mHandler = new MessageListHandler();
|
|
private ControllerResults mControllerCallback = new ControllerResults();
|
|
|
|
/**
|
|
* Reduced mailbox projection used by AccountsAdapter
|
|
*/
|
|
public final static int MAILBOX_COLUMN_ID = 0;
|
|
public final static int MAILBOX_DISPLAY_NAME = 1;
|
|
public final static int MAILBOX_ACCOUNT_KEY = 2;
|
|
public final static int MAILBOX_TYPE = 3;
|
|
public final static int MAILBOX_UNREAD_COUNT = 4;
|
|
public final static int MAILBOX_FLAG_VISIBLE = 5;
|
|
public final static int MAILBOX_FLAGS = 6;
|
|
|
|
public final static String[] MAILBOX_PROJECTION = new String[] {
|
|
EmailContent.RECORD_ID, MailboxColumns.DISPLAY_NAME,
|
|
MailboxColumns.ACCOUNT_KEY, MailboxColumns.TYPE,
|
|
MailboxColumns.UNREAD_COUNT,
|
|
MailboxColumns.FLAG_VISIBLE, MailboxColumns.FLAGS
|
|
};
|
|
|
|
private static final String FAVORITE_COUNT_SELECTION =
|
|
MessageColumns.FLAG_FAVORITE + "= 1";
|
|
|
|
private static final String MAILBOX_TYPE_SELECTION =
|
|
MailboxColumns.TYPE + " =?";
|
|
|
|
private static final String[] MAILBOX_SUM_OF_UNREAD_COUNT_PROJECTION = new String [] {
|
|
"sum(" + MailboxColumns.UNREAD_COUNT + ")"
|
|
};
|
|
|
|
private static final String MAILBOX_INBOX_SELECTION =
|
|
MailboxColumns.ACCOUNT_KEY + " =?" + " AND " + MailboxColumns.TYPE +" = "
|
|
+ Mailbox.TYPE_INBOX;
|
|
|
|
private static final int MAILBOX_UNREAD_COUNT_COLUMN_UNREAD_COUNT = 0;
|
|
private static final String[] MAILBOX_UNREAD_COUNT_PROJECTION = new String [] {
|
|
MailboxColumns.UNREAD_COUNT
|
|
};
|
|
|
|
private static final int[] mColorChipResIds = 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,
|
|
};
|
|
|
|
/**
|
|
* Start the Accounts list activity. Uses the CLEAR_TOP flag which means that other stacked
|
|
* activities may be killed in order to get back to Accounts.
|
|
*/
|
|
public static void actionShowAccounts(Context context) {
|
|
Intent i = new Intent(context, AccountFolderList.class);
|
|
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
context.startActivity(i);
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(Bundle icicle) {
|
|
super.onCreate(icicle);
|
|
|
|
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
|
|
setContentView(R.layout.account_folder_list);
|
|
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,
|
|
R.layout.list_title);
|
|
|
|
mProgressIcon = (ProgressBar) findViewById(R.id.title_progress_icon);
|
|
|
|
mListView = getListView();
|
|
mListView.setItemsCanFocus(false);
|
|
mListView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);
|
|
mListView.setOnItemClickListener(this);
|
|
mListView.setLongClickable(true);
|
|
registerForContextMenu(mListView);
|
|
|
|
if (icicle != null && icicle.containsKey(ICICLE_SELECTED_ACCOUNT)) {
|
|
mSelectedContextAccount = (Account) icicle.getParcelable(ICICLE_SELECTED_ACCOUNT);
|
|
}
|
|
|
|
((TextView) findViewById(R.id.title_left_text)).setText(R.string.app_name);
|
|
|
|
mLoadAccountsTask = (LoadAccountsTask) new LoadAccountsTask().execute();
|
|
}
|
|
|
|
@Override
|
|
public void onSaveInstanceState(Bundle outState) {
|
|
super.onSaveInstanceState(outState);
|
|
if (mSelectedContextAccount != null) {
|
|
outState.putParcelable(ICICLE_SELECTED_ACCOUNT, mSelectedContextAccount);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
super.onPause();
|
|
Controller.getInstance(getApplication()).removeResultCallback(mControllerCallback);
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
|
|
NotificationManager notifMgr = (NotificationManager)
|
|
getSystemService(Context.NOTIFICATION_SERVICE);
|
|
notifMgr.cancel(1);
|
|
|
|
Controller.getInstance(getApplication()).addResultCallback(mControllerCallback);
|
|
|
|
if (mLoadAccountsTask != null &&
|
|
mLoadAccountsTask.getStatus() != LoadAccountsTask.Status.FINISHED) {
|
|
mLoadAccountsTask.cancel(true);
|
|
}
|
|
mLoadAccountsTask = (LoadAccountsTask) new LoadAccountsTask().execute();
|
|
|
|
// TODO: What updates do we need to auto-trigger, now that we have mailboxes in view?
|
|
}
|
|
|
|
@Override
|
|
protected void onDestroy() {
|
|
super.onDestroy();
|
|
|
|
if (mLoadAccountsTask != null &&
|
|
mLoadAccountsTask.getStatus() != LoadAccountsTask.Status.FINISHED) {
|
|
mLoadAccountsTask.cancel(true);
|
|
mLoadAccountsTask = null;
|
|
}
|
|
}
|
|
|
|
public void onClick(View v) {
|
|
switch (v.getId()) {
|
|
case R.id.button_compose:
|
|
onCompose(-1);
|
|
break;
|
|
case R.id.button_refresh:
|
|
onRefresh(-1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
if (mListAdapter.isMailbox(position)) {
|
|
MessageList.actionHandleMailbox(this, id);
|
|
} else if (mListAdapter.isAccount(position)) {
|
|
MessageList.actionHandleAccount(this, id, Mailbox.TYPE_INBOX);
|
|
}
|
|
}
|
|
|
|
private static int getUnreadCountByMailboxType(Context context, int type) {
|
|
int count = 0;
|
|
Cursor c = context.getContentResolver().query(Mailbox.CONTENT_URI,
|
|
MAILBOX_SUM_OF_UNREAD_COUNT_PROJECTION,
|
|
MAILBOX_TYPE_SELECTION,
|
|
new String[] { String.valueOf(type) }, null);
|
|
|
|
try {
|
|
if (c.moveToFirst()) {
|
|
return c.getInt(0);
|
|
}
|
|
} finally {
|
|
c.close();
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Build the group and child cursors that support the summary views (aka "at a glance").
|
|
*
|
|
* This is a placeholder implementation with significant problems that need to be addressed:
|
|
*
|
|
* TODO: We should only show summary mailboxes if they are non-empty. So there needs to be
|
|
* a more dynamic child-cursor here, probably listening for update notifications on a number
|
|
* of other internally-held queries such as count-of-inbox, count-of-unread, etc.
|
|
*
|
|
* TODO: This simple list is incomplete. For example, we probably want drafts, outbox, and
|
|
* (maybe) sent (again, these would be displayed only when non-empty).
|
|
*
|
|
* TODO: We need a way to count total unread in all inboxes (probably with some provider help)
|
|
*
|
|
* TODO: We need a way to count total # messages in all other summary boxes (probably with
|
|
* some provider help).
|
|
*
|
|
* TODO use narrower account projection (see LoadAccountsTask)
|
|
*/
|
|
private MatrixCursor getSummaryChildCursor() {
|
|
MatrixCursor childCursor = new MatrixCursor(MAILBOX_PROJECTION);
|
|
int count;
|
|
RowBuilder row;
|
|
// TYPE_INBOX
|
|
count = getUnreadCountByMailboxType(this, Mailbox.TYPE_INBOX);
|
|
if (count > 0) {
|
|
row = childCursor.newRow();
|
|
row.add(Long.valueOf(Mailbox.QUERY_ALL_INBOXES)); // MAILBOX_COLUMN_ID = 0;
|
|
row.add(getString(R.string.account_folder_list_summary_inbox)); // MAILBOX_DISPLAY_NAME
|
|
row.add(null); // MAILBOX_ACCOUNT_KEY = 2;
|
|
row.add(Integer.valueOf(Mailbox.TYPE_INBOX)); // MAILBOX_TYPE = 3;
|
|
// This value is 0 because count doesn't the number of messages in INBOX
|
|
row.add(Integer.valueOf(count)); // MAILBOX_UNREAD_COUNT = 4;
|
|
}
|
|
// TYPE_MAIL (FAVORITES)
|
|
count = EmailContent.count(this, Message.CONTENT_URI, FAVORITE_COUNT_SELECTION, null);
|
|
if (count > 0) {
|
|
row = childCursor.newRow();
|
|
row.add(Long.valueOf(Mailbox.QUERY_ALL_FAVORITES)); // MAILBOX_COLUMN_ID = 0;
|
|
// MAILBOX_DISPLAY_NAME
|
|
row.add(getString(R.string.account_folder_list_summary_favorite));
|
|
row.add(null); // MAILBOX_ACCOUNT_KEY = 2;
|
|
row.add(Integer.valueOf(Mailbox.TYPE_MAIL)); // MAILBOX_TYPE = 3;
|
|
row.add(Integer.valueOf(count)); // MAILBOX_UNREAD_COUNT = 4;
|
|
}
|
|
// TYPE_DRAFTS
|
|
count = getUnreadCountByMailboxType(this, Mailbox.TYPE_DRAFTS);
|
|
if (count > 0) {
|
|
row = childCursor.newRow();
|
|
row.add(Long.valueOf(Mailbox.QUERY_ALL_DRAFTS)); // MAILBOX_COLUMN_ID = 0;
|
|
row.add(getString(R.string.account_folder_list_summary_drafts));// MAILBOX_DISPLAY_NAME
|
|
row.add(null); // MAILBOX_ACCOUNT_KEY = 2;
|
|
row.add(Integer.valueOf(Mailbox.TYPE_DRAFTS)); // MAILBOX_TYPE = 3;
|
|
row.add(Integer.valueOf(count)); // MAILBOX_UNREAD_COUNT = 4;
|
|
}
|
|
// TYPE_OUTBOX
|
|
count = getUnreadCountByMailboxType(this, Mailbox.TYPE_OUTBOX);
|
|
if (count > 0) {
|
|
row = childCursor.newRow();
|
|
row.add(Long.valueOf(Mailbox.QUERY_ALL_OUTBOX)); // MAILBOX_COLUMN_ID = 0;
|
|
row.add(getString(R.string.account_folder_list_summary_outbox));// MAILBOX_DISPLAY_NAME
|
|
row.add(null); // MAILBOX_ACCOUNT_KEY = 2;
|
|
row.add(Integer.valueOf(Mailbox.TYPE_OUTBOX)); // MAILBOX_TYPE = 3;
|
|
row.add(Integer.valueOf(count)); // MAILBOX_UNREAD_COUNT = 4;
|
|
}
|
|
return childCursor;
|
|
}
|
|
|
|
/**
|
|
* Async task to handle the accounts query outside of the UI thread
|
|
*/
|
|
private class LoadAccountsTask extends AsyncTask<Void, Void, Cursor[]> {
|
|
|
|
@Override
|
|
protected Cursor[] doInBackground(Void... params) {
|
|
// Create the summaries cursor
|
|
Cursor c1 = getSummaryChildCursor();
|
|
|
|
// TODO use a custom projection and don't have to sample all of these columns
|
|
Cursor c2 = AccountFolderList.this.managedQuery(
|
|
EmailContent.Account.CONTENT_URI,
|
|
EmailContent.Account.CONTENT_PROJECTION,
|
|
null, null, null);
|
|
|
|
return new Cursor[] { c1, c2 };
|
|
}
|
|
|
|
@Override
|
|
protected void onPostExecute(Cursor[] cursors) {
|
|
if (cursors[1].isClosed()) {
|
|
return;
|
|
}
|
|
mListAdapter = AccountsAdapter.getInstance(cursors[0], cursors[1],
|
|
AccountFolderList.this);
|
|
mListView.setAdapter(mListAdapter);
|
|
}
|
|
}
|
|
|
|
private void onAddNewAccount() {
|
|
AccountSetupBasics.actionNewAccount(this);
|
|
}
|
|
|
|
private void onEditAccount(long accountId) {
|
|
AccountSettings.actionSettings(this, accountId);
|
|
}
|
|
|
|
/**
|
|
* Refresh one or all accounts
|
|
* @param accountId A specific id to refresh folders only, or -1 to refresh everything
|
|
*/
|
|
private void onRefresh(long accountId) {
|
|
if (accountId == -1) {
|
|
// TODO implement a suitable "Refresh all accounts" / "check mail" comment in Controller
|
|
// TODO this is temp
|
|
Toast.makeText(this,
|
|
"Please longpress an account to refresh it", Toast.LENGTH_LONG).show();
|
|
} else {
|
|
mHandler.progress(true);
|
|
Controller.getInstance(getApplication()).updateMailboxList(
|
|
accountId, mControllerCallback);
|
|
}
|
|
}
|
|
|
|
private void onCompose(long accountId) {
|
|
if (accountId == -1) {
|
|
accountId = Account.getDefaultAccountId(this);
|
|
}
|
|
if (accountId != -1) {
|
|
MessageCompose.actionCompose(this, accountId);
|
|
} else {
|
|
onAddNewAccount();
|
|
}
|
|
}
|
|
|
|
private void onDeleteAccount(long accountId) {
|
|
mSelectedContextAccount = Account.restoreAccountWithId(this, accountId);
|
|
showDialog(DIALOG_REMOVE_ACCOUNT);
|
|
}
|
|
|
|
@Override
|
|
public Dialog onCreateDialog(int id) {
|
|
switch (id) {
|
|
case DIALOG_REMOVE_ACCOUNT:
|
|
return createRemoveAccountDialog();
|
|
}
|
|
return super.onCreateDialog(id);
|
|
}
|
|
|
|
private Dialog createRemoveAccountDialog() {
|
|
return new AlertDialog.Builder(this)
|
|
.setIcon(android.R.drawable.ic_dialog_alert)
|
|
.setTitle(R.string.account_delete_dlg_title)
|
|
.setMessage(getString(R.string.account_delete_dlg_instructions_fmt,
|
|
mSelectedContextAccount.getDisplayName()))
|
|
.setPositiveButton(R.string.okay_action, new DialogInterface.OnClickListener() {
|
|
public void onClick(DialogInterface dialog, int whichButton) {
|
|
dismissDialog(DIALOG_REMOVE_ACCOUNT);
|
|
try {
|
|
// Delete Remote store at first.
|
|
Store.getInstance(
|
|
mSelectedContextAccount.getStoreUri(AccountFolderList.this),
|
|
getApplication(), null).delete();
|
|
// Remove the Store instance from cache.
|
|
Store.removeInstance(mSelectedContextAccount.getStoreUri(
|
|
AccountFolderList.this));
|
|
Uri uri = ContentUris.withAppendedId(
|
|
EmailContent.Account.CONTENT_URI, mSelectedContextAccount.mId);
|
|
AccountFolderList.this.getContentResolver().delete(uri, null, null);
|
|
} catch (Exception e) {
|
|
// Ignore
|
|
}
|
|
Email.setServicesEnabled(AccountFolderList.this);
|
|
|
|
// Jump to account setup if the last/only account was deleted
|
|
int numAccounts = EmailContent.count(AccountFolderList.this,
|
|
Account.CONTENT_URI, null, null);
|
|
if (numAccounts == 0) {
|
|
AccountSetupBasics.actionNewAccount(AccountFolderList.this);
|
|
finish();
|
|
}
|
|
}
|
|
})
|
|
.setNegativeButton(R.string.cancel_action, new DialogInterface.OnClickListener() {
|
|
public void onClick(DialogInterface dialog, int whichButton) {
|
|
dismissDialog(DIALOG_REMOVE_ACCOUNT);
|
|
}
|
|
})
|
|
.create();
|
|
}
|
|
|
|
@Override
|
|
public boolean onContextItemSelected(MenuItem item) {
|
|
AdapterView.AdapterContextMenuInfo menuInfo =
|
|
(AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
|
|
|
|
if (mListAdapter.isMailbox(menuInfo.position)) {
|
|
Cursor c = (Cursor) mListView.getItemAtPosition(menuInfo.position);
|
|
long id = c.getLong(MAILBOX_COLUMN_ID);
|
|
switch (item.getItemId()) {
|
|
case R.id.open_folder:
|
|
MessageList.actionHandleMailbox(this, id);
|
|
break;
|
|
case R.id.check_mail:
|
|
onRefresh(-1);
|
|
break;
|
|
}
|
|
return false;
|
|
} else if (mListAdapter.isAccount(menuInfo.position)) {
|
|
Cursor c = (Cursor) mListView.getItemAtPosition(menuInfo.position);
|
|
long accountId = c.getLong(Account.CONTENT_ID_COLUMN);
|
|
switch (item.getItemId()) {
|
|
case R.id.open:
|
|
MailboxList.actionHandleAccount(this, accountId);
|
|
break;
|
|
case R.id.compose:
|
|
onCompose(accountId);
|
|
break;
|
|
case R.id.refresh_account:
|
|
onRefresh(accountId);
|
|
break;
|
|
case R.id.edit_account:
|
|
onEditAccount(accountId);
|
|
break;
|
|
case R.id.delete_account:
|
|
onDeleteAccount(accountId);
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
switch (item.getItemId()) {
|
|
case R.id.add_new_account:
|
|
onAddNewAccount();
|
|
break;
|
|
case R.id.check_mail:
|
|
onRefresh(-1);
|
|
break;
|
|
case R.id.compose:
|
|
onCompose(-1);
|
|
break;
|
|
default:
|
|
return super.onOptionsItemSelected(item);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean onCreateOptionsMenu(Menu menu) {
|
|
super.onCreateOptionsMenu(menu);
|
|
getMenuInflater().inflate(R.menu.account_folder_list_option, menu);
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo info) {
|
|
super.onCreateContextMenu(menu, v, info);
|
|
AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) info;
|
|
if (mListAdapter.isMailbox(menuInfo.position)) {
|
|
Cursor c = (Cursor) mListView.getItemAtPosition(menuInfo.position);
|
|
String displayName = c.getString(Account.CONTENT_DISPLAY_NAME_COLUMN);
|
|
menu.setHeaderTitle(displayName);
|
|
getMenuInflater().inflate(R.menu.account_folder_list_smart_folder_context, menu);
|
|
} else if (mListAdapter.isAccount(menuInfo.position)) {
|
|
Cursor c = (Cursor) mListView.getItemAtPosition(menuInfo.position);
|
|
String accountName = c.getString(Account.CONTENT_DISPLAY_NAME_COLUMN);
|
|
menu.setHeaderTitle(accountName);
|
|
getMenuInflater().inflate(R.menu.account_folder_list_context, menu);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
|
if (event.getKeyCode() == secretKeyCodes[mSecretKeyCodeIndex]) {
|
|
mSecretKeyCodeIndex++;
|
|
if (mSecretKeyCodeIndex == secretKeyCodes.length) {
|
|
mSecretKeyCodeIndex = 0;
|
|
startActivity(new Intent(this, Debug.class));
|
|
}
|
|
} else {
|
|
mSecretKeyCodeIndex = 0;
|
|
}
|
|
return super.onKeyDown(keyCode, event);
|
|
}
|
|
|
|
/**
|
|
* Handler for UI-thread operations (when called from callbacks or any other threads)
|
|
*/
|
|
class MessageListHandler extends Handler {
|
|
private static final int MSG_PROGRESS = 1;
|
|
|
|
@Override
|
|
public void handleMessage(android.os.Message msg) {
|
|
switch (msg.what) {
|
|
case MSG_PROGRESS:
|
|
boolean showProgress = (msg.arg1 != 0);
|
|
if (showProgress) {
|
|
mProgressIcon.setVisibility(View.VISIBLE);
|
|
} else {
|
|
mProgressIcon.setVisibility(View.GONE);
|
|
}
|
|
break;
|
|
default:
|
|
super.handleMessage(msg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Call from any thread to start/stop progress indicator(s)
|
|
* @param progress true to start, false to stop
|
|
*/
|
|
public void progress(boolean progress) {
|
|
android.os.Message msg = android.os.Message.obtain();
|
|
msg.what = MSG_PROGRESS;
|
|
msg.arg1 = progress ? 1 : 0;
|
|
sendMessage(msg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback for async Controller results.
|
|
*/
|
|
private class ControllerResults implements Controller.Result {
|
|
public void updateMailboxListCallback(MessagingException result, long accountKey,
|
|
int progress) {
|
|
updateProgress(result, progress);
|
|
}
|
|
|
|
public void updateMailboxCallback(MessagingException result, long accountKey,
|
|
long mailboxKey, int progress, int numNewMessages) {
|
|
updateProgress(result, progress);
|
|
}
|
|
|
|
public void loadMessageForViewCallback(MessagingException result, long messageId,
|
|
int progress) {
|
|
}
|
|
|
|
public void loadAttachmentCallback(MessagingException result, long messageId,
|
|
long attachmentId, int progress) {
|
|
}
|
|
|
|
public void serviceCheckMailCallback(MessagingException result, long accountId,
|
|
long mailboxId, int progress, long tag) {
|
|
updateProgress(result, progress);
|
|
}
|
|
|
|
public void sendMailCallback(MessagingException result, long accountId, long messageId,
|
|
int progress) {
|
|
}
|
|
|
|
private void updateProgress(MessagingException result, int progress) {
|
|
if (result != null || progress == 100) {
|
|
mHandler.progress(false);
|
|
} else if (progress == 0) {
|
|
mHandler.progress(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* package */ static class AccountsAdapter extends CursorAdapter {
|
|
|
|
Context mContext;
|
|
private LayoutInflater mInflater;
|
|
private int mMailboxesCount;
|
|
private int mSeparatorPosition;
|
|
|
|
public static AccountsAdapter getInstance(Cursor mailboxesCursor, Cursor accountsCursor,
|
|
Context context) {
|
|
Cursor[] cursors = new Cursor[] { mailboxesCursor, accountsCursor };
|
|
Cursor mc = new MergeCursor(cursors);
|
|
return new AccountsAdapter(mc, context, mailboxesCursor.getCount());
|
|
}
|
|
|
|
public AccountsAdapter(Cursor c, Context context, int mailboxesCount) {
|
|
super(context, c, true);
|
|
mContext = context;
|
|
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
|
mMailboxesCount = mailboxesCount;
|
|
mSeparatorPosition = mailboxesCount;
|
|
}
|
|
|
|
public boolean isMailbox(int position) {
|
|
return position < mMailboxesCount;
|
|
}
|
|
|
|
public boolean isAccount(int position) {
|
|
return position >= mMailboxesCount;
|
|
}
|
|
|
|
/**
|
|
* This is used as a callback from the list items, for clicks in the folder "button"
|
|
*
|
|
* @param itemView the item in which the click occurred
|
|
*/
|
|
public void onClickFolder(AccountFolderListItem itemView) {
|
|
MailboxList.actionHandleAccount(mContext, itemView.mAccountId);
|
|
}
|
|
|
|
@Override
|
|
public void bindView(View view, Context context, Cursor cursor) {
|
|
if (cursor.getPosition() < mMailboxesCount) {
|
|
bindMailboxItem(view, context, cursor, false);
|
|
} else {
|
|
bindAccountItem(view, context, cursor, false);
|
|
}
|
|
}
|
|
|
|
private void bindMailboxItem(View view, Context context, Cursor cursor, boolean isLastChild)
|
|
{
|
|
// Reset the view (in case it was recycled) and prepare for binding
|
|
AccountFolderListItem itemView = (AccountFolderListItem) view;
|
|
itemView.bindViewInit(this, false);
|
|
|
|
// Invisible (not "gone") to maintain spacing
|
|
view.findViewById(R.id.chip).setVisibility(View.INVISIBLE);
|
|
|
|
int type = cursor.getInt(MAILBOX_TYPE);
|
|
String text = Utility.FolderProperties.getInstance(context).getDisplayName(type);
|
|
if (text == null) {
|
|
text = cursor.getString(MAILBOX_DISPLAY_NAME);
|
|
}
|
|
if (text != null) {
|
|
TextView nameView = (TextView) view.findViewById(R.id.name);
|
|
nameView.setText(text);
|
|
}
|
|
|
|
// TODO get/track live folder status
|
|
text = null;
|
|
TextView statusView = (TextView) view.findViewById(R.id.status);
|
|
if (text != null) {
|
|
statusView.setText(text);
|
|
statusView.setVisibility(View.VISIBLE);
|
|
} else {
|
|
statusView.setVisibility(View.GONE);
|
|
}
|
|
|
|
int count = -1;
|
|
text = cursor.getString(MAILBOX_UNREAD_COUNT);
|
|
if (text != null) {
|
|
count = Integer.valueOf(text);
|
|
}
|
|
TextView countView = (TextView) view.findViewById(R.id.new_message_count);
|
|
// If the unread count is zero, not to show countView.
|
|
if (count > 0) {
|
|
countView.setVisibility(View.VISIBLE);
|
|
countView.setText(text);
|
|
} else {
|
|
countView.setVisibility(View.GONE);
|
|
}
|
|
int id = cursor.getInt(MAILBOX_COLUMN_ID);
|
|
if (id == Mailbox.QUERY_ALL_FAVORITES
|
|
|| id == Mailbox.QUERY_ALL_DRAFTS
|
|
|| id == Mailbox.QUERY_ALL_OUTBOX) {
|
|
countView.setBackgroundResource(R.drawable.ind_sum);
|
|
} else {
|
|
countView.setBackgroundResource(R.drawable.ind_unread);
|
|
}
|
|
// Padding should be reset after setBackgroundResource
|
|
countView.setPadding(2, 0, 2, 0);
|
|
|
|
view.findViewById(R.id.folder_button).setVisibility(View.GONE);
|
|
view.findViewById(R.id.folder_icon).setVisibility(View.VISIBLE);
|
|
((ImageView)view.findViewById(R.id.folder_icon)).setImageDrawable(
|
|
Utility.FolderProperties.getInstance(context).getIconIds(type));
|
|
}
|
|
|
|
private void bindAccountItem(View view, Context context, Cursor cursor, boolean isExpanded)
|
|
{
|
|
// Reset the view (in case it was recycled) and prepare for binding
|
|
AccountFolderListItem itemView = (AccountFolderListItem) view;
|
|
itemView.bindViewInit(this, true);
|
|
itemView.mAccountId = cursor.getLong(Account.CONTENT_ID_COLUMN);
|
|
|
|
long accountId = cursor.getLong(Account.CONTENT_ID_COLUMN);
|
|
View chipView = view.findViewById(R.id.chip);
|
|
int chipResId = mColorChipResIds[(int)accountId % mColorChipResIds.length];
|
|
chipView.setBackgroundResource(chipResId);
|
|
chipView.setVisibility(View.VISIBLE);
|
|
|
|
String text = cursor.getString(Account.CONTENT_DISPLAY_NAME_COLUMN);
|
|
if (text != null) {
|
|
TextView descriptionView = (TextView) view.findViewById(R.id.name);
|
|
descriptionView.setText(text);
|
|
}
|
|
|
|
text = cursor.getString(Account.CONTENT_EMAIL_ADDRESS_COLUMN);
|
|
if (text != null) {
|
|
TextView emailView = (TextView) view.findViewById(R.id.status);
|
|
emailView.setText(text);
|
|
emailView.setVisibility(View.VISIBLE);
|
|
}
|
|
|
|
int unreadMessageCount = 0;
|
|
Cursor c = context.getContentResolver().query(Mailbox.CONTENT_URI,
|
|
MAILBOX_UNREAD_COUNT_PROJECTION,
|
|
MAILBOX_INBOX_SELECTION,
|
|
new String[] { String.valueOf(accountId) }, null);
|
|
|
|
try {
|
|
if (c.moveToFirst()) {
|
|
String count = c.getString(MAILBOX_UNREAD_COUNT_COLUMN_UNREAD_COUNT);
|
|
if (count != null) {
|
|
unreadMessageCount = Integer.valueOf(count);
|
|
}
|
|
}
|
|
} finally {
|
|
c.close();
|
|
}
|
|
|
|
TextView countView = (TextView) view.findViewById(R.id.new_message_count);
|
|
if (unreadMessageCount > 0) {
|
|
countView.setText(String.valueOf(unreadMessageCount));
|
|
countView.setVisibility(View.VISIBLE);
|
|
} else {
|
|
countView.setVisibility(View.GONE);
|
|
}
|
|
countView.setBackgroundResource(R.drawable.ind_unread);
|
|
// Padding should be reset after setBackgroundResource
|
|
countView.setPadding(2, 0, 2, 0);
|
|
|
|
view.findViewById(R.id.folder_icon).setVisibility(View.GONE);
|
|
view.findViewById(R.id.folder_button).setVisibility(View.VISIBLE);
|
|
}
|
|
|
|
@Override
|
|
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
|
return mInflater.inflate(R.layout.account_folder_list_item, parent, false);
|
|
}
|
|
|
|
/*
|
|
* The following series of overrides insert the "Accounts" separator
|
|
*/
|
|
|
|
/**
|
|
* Prevents the separator view from recycling into the other views
|
|
*/
|
|
@Override
|
|
public int getItemViewType(int position) {
|
|
if (position == mSeparatorPosition) {
|
|
return IGNORE_ITEM_VIEW_TYPE;
|
|
}
|
|
return super.getItemViewType(position);
|
|
}
|
|
|
|
/**
|
|
* Injects the separator view when required, and fudges the cursor for other views
|
|
*/
|
|
@Override
|
|
public View getView(int position, View convertView, ViewGroup parent) {
|
|
if (!mDataValid) {
|
|
throw new IllegalStateException("cursor invalid");
|
|
}
|
|
|
|
// Handle the separator here - create & bind
|
|
if (position == mSeparatorPosition) {
|
|
TextView view;
|
|
view = (TextView) mInflater.inflate(R.layout.list_separator, parent, false);
|
|
view.setText(R.string.account_folder_list_separator_accounts);
|
|
return view;
|
|
}
|
|
|
|
if (!mCursor.moveToPosition(getRealPosition(position))) {
|
|
throw new IllegalStateException("cursor failed move to " + position);
|
|
}
|
|
|
|
View v;
|
|
if (convertView == null) {
|
|
v = newView(mContext, mCursor, parent);
|
|
} else {
|
|
v = convertView;
|
|
}
|
|
bindView(v, mContext, mCursor);
|
|
return v;
|
|
}
|
|
|
|
/**
|
|
* Forces navigation to skip over the separator
|
|
*/
|
|
@Override
|
|
public boolean areAllItemsEnabled() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Forces navigation to skip over the separator
|
|
*/
|
|
@Override
|
|
public boolean isEnabled(int position) {
|
|
return position != mSeparatorPosition;
|
|
}
|
|
|
|
/**
|
|
* Adjusts list count to include separator
|
|
*/
|
|
@Override
|
|
public int getCount() {
|
|
int count = super.getCount();
|
|
if (mDataValid && (mSeparatorPosition != ListView.INVALID_POSITION)) {
|
|
count += 1;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Converts list position to cursor position
|
|
*/
|
|
private int getRealPosition(int pos) {
|
|
if (mSeparatorPosition == ListView.INVALID_POSITION) {
|
|
// No separator, identity map
|
|
return pos;
|
|
} else if (pos <= mSeparatorPosition) {
|
|
// Before or at the separator, identity map
|
|
return pos;
|
|
} else {
|
|
// After the separator, remove 1 from the pos to get the real underlying pos
|
|
return pos - 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the item using external position numbering (no separator)
|
|
*/
|
|
@Override
|
|
public Object getItem(int pos) {
|
|
return super.getItem(getRealPosition(pos));
|
|
}
|
|
|
|
/**
|
|
* Returns the item id using external position numbering (no separator)
|
|
*/
|
|
@Override
|
|
public long getItemId(int pos) {
|
|
return super.getItemId(getRealPosition(pos));
|
|
}
|
|
}
|
|
}
|
|
|
|
|