MailboxListFragment.java revision 9ddd1e7fa4b700326f00e406219b1ac584dee5a0
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.email.activity; 18 19import com.android.email.R; 20import com.android.email.Utility; 21import com.android.email.provider.EmailContent; 22import com.android.email.provider.EmailContent.Mailbox; 23import com.android.email.provider.EmailContent.MailboxColumns; 24import com.android.email.provider.EmailContent.Message; 25import com.android.email.provider.EmailContent.MessageColumns; 26 27import android.app.Activity; 28import android.app.ListFragment; 29import android.database.Cursor; 30import android.os.AsyncTask; 31import android.os.Bundle; 32import android.view.ContextMenu; 33import android.view.ContextMenu.ContextMenuInfo; 34import android.view.MenuItem; 35import android.view.View; 36import android.widget.AdapterView; 37import android.widget.AdapterView.OnItemClickListener; 38import android.widget.ListView; 39 40import java.security.InvalidParameterException; 41 42/** 43 * This fragment presents a list of mailboxes for a given account. The "API" includes the 44 * following elements which must be provided by the host Activity. 45 * 46 * - call bindActivityInfo() to provide the account ID and set callbacks 47 * - provide callbacks for onOpen and onRefresh 48 * - pass-through implementations of onCreateContextMenu() and onContextItemSelected() (temporary) 49 */ 50public class MailboxListFragment extends ListFragment implements OnItemClickListener { 51 52 private static final String MAILBOX_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?" + 53 " AND " + MailboxColumns.TYPE + "<" + Mailbox.TYPE_NOT_EMAIL + 54 " AND " + MailboxColumns.FLAG_VISIBLE + "=1"; 55 private static final String MESSAGE_MAILBOX_ID_SELECTION = MessageColumns.MAILBOX_KEY + "=?"; 56 57 // Account & mailboxes access 58 private long mAccountId = -1; 59 private LoadMailboxesTask mLoadMailboxesTask; 60 private MessageCountTask mMessageCountTask; 61 private long mDraftMailboxKey = -1; 62 private long mTrashMailboxKey = -1; 63 64 // UI Support 65 private Activity mActivity; 66 private MailboxesAdapter mListAdapter; 67 private Callback mCallback = EmptyCallback.INSTANCE; 68 69 private boolean mStarted; 70 71 /** 72 * Callback interface that owning activities must implement 73 */ 74 public interface Callback { 75 /** @deprecated There'll be no context menu, so no refresh. */ 76 public void onRefresh(long accountId, long mailboxId); 77 public void onMailboxSelected(long accountId, long mailboxId); 78 } 79 80 private static class EmptyCallback implements Callback { 81 public static final Callback INSTANCE = new EmptyCallback(); 82 @Override 83 public void onMailboxSelected(long accountId, long mailboxId) { 84 } 85 86 /** @deprecated */ 87 @Override 88 public void onRefresh(long accountId, long mailboxId) { 89 } 90 } 91 92 /** 93 * Called to do initial creation of a fragment. This is called after 94 * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}. 95 */ 96 @Override 97 public void onCreate(Bundle savedInstanceState) { 98 super.onCreate(savedInstanceState); 99 100 mActivity = getActivity(); 101 mListAdapter = new MailboxesAdapter(mActivity); 102 } 103 104 @Override 105 public void onActivityCreated(Bundle savedInstanceState) { 106 super.onActivityCreated(savedInstanceState); 107 108 ListView listView = getListView(); 109 listView.setOnItemClickListener(this); 110 listView.setItemsCanFocus(false); 111 registerForContextMenu(listView); 112 } 113 114 public void setCallback(Callback callback) { 115 mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback; 116 } 117 118 /** 119 * @param accountId the account we're looking at 120 */ 121 public void openMailboxes(long accountId) { 122 if (accountId == -1) { 123 throw new InvalidParameterException(); 124 } 125 if (mAccountId == accountId) { 126 return; 127 } 128 mAccountId = accountId; 129 if (mStarted) { 130 startLoading(); 131 } 132 } 133 134 /** 135 * Called when the Fragment is visible to the user. 136 */ 137 @Override 138 public void onStart() { 139 super.onStart(); 140 mStarted = true; 141 if (mAccountId != -1) { 142 startLoading(); 143 } 144 } 145 146 /** 147 * Called when the fragment is visible to the user and actively running. 148 */ 149 @Override 150 public void onResume() { 151 mStarted = false; 152 super.onResume(); 153 updateMessageCount(); 154 } 155 156 /** 157 * Called when the Fragment is no longer started. 158 */ 159 @Override 160 public void onStop() { 161 super.onStop(); 162 cancelAllTasks(); 163 } 164 165 /** 166 * Called when the fragment is no longer in use. 167 */ 168 @Override 169 public void onDestroy() { 170 super.onDestroy(); 171 172 mListAdapter.changeCursor(null); 173 } 174 175 private void cancelAllTasks() { 176 Utility.cancelTaskInterrupt(mLoadMailboxesTask); 177 mLoadMailboxesTask = null; 178 Utility.cancelTaskInterrupt(mMessageCountTask); 179 mMessageCountTask = null; 180 } 181 182 private void startLoading() { 183 cancelAllTasks(); 184 185 // Clear the list. (ListFragment will show the "Loading" animation) 186 setListAdapter(null); 187 setListShown(false); 188 189 mLoadMailboxesTask = new LoadMailboxesTask(mAccountId); 190 mLoadMailboxesTask.execute(); 191 } 192 193 /** 194 * This is called via the activity 195 * TODO This will be removed when possible 196 */ 197 @Override 198 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo info) { 199 AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) info; 200 Cursor c = (Cursor) getListView().getItemAtPosition(menuInfo.position); 201 String folderName = Utility.FolderProperties.getInstance(mActivity) 202 .getDisplayName(Integer.valueOf(c.getString(mListAdapter.COLUMN_TYPE))); 203 if (folderName == null) { 204 folderName = c.getString(mListAdapter.COLUMN_DISPLAY_NAME); 205 } 206 207 menu.setHeaderTitle(folderName); 208 mActivity.getMenuInflater().inflate(R.menu.mailbox_list_context, menu); 209 } 210 211 /** 212 * This is called via the activity 213 * TODO This will be removed when possible 214 */ 215 @Override 216 public boolean onContextItemSelected(MenuItem item) { 217 AdapterView.AdapterContextMenuInfo info = 218 (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); 219 220 switch (item.getItemId()) { 221 case R.id.refresh: 222 mCallback.onRefresh(mAccountId, info.id); 223 return true; 224 case R.id.open: 225 mCallback.onMailboxSelected(mAccountId, info.id); 226 return true; 227 } 228 return false; 229 } 230 231 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 232 mCallback.onMailboxSelected(mAccountId, id); 233 } 234 235 /** 236 * Async task for loading the mailboxes for a given account 237 */ 238 private class LoadMailboxesTask extends AsyncTask<Void, Void, Object[]> { 239 240 private long mAccountKey; 241 242 /** 243 * Special constructor to cache some local info 244 */ 245 public LoadMailboxesTask(long accountId) { 246 mAccountKey = accountId; 247 mDraftMailboxKey = -1; 248 mTrashMailboxKey = -1; 249 } 250 251 @Override 252 protected Object[] doInBackground(Void... params) { 253 long draftMailboxKey = -1; 254 long trashMailboxKey = -1; 255 Cursor c = mActivity.managedQuery( 256 EmailContent.Mailbox.CONTENT_URI, 257 mListAdapter.PROJECTION, 258 MAILBOX_SELECTION, 259 new String[] { String.valueOf(mAccountKey) }, 260 MailboxColumns.TYPE + "," + MailboxColumns.DISPLAY_NAME); 261 c.moveToPosition(-1); 262 while (c.moveToNext()) { 263 long mailboxId = c.getInt(mListAdapter.COLUMN_ID); 264 switch (c.getInt(mListAdapter.COLUMN_TYPE)) { 265 case Mailbox.TYPE_DRAFTS: 266 draftMailboxKey = mailboxId; 267 break; 268 case Mailbox.TYPE_TRASH: 269 trashMailboxKey = mailboxId; 270 break; 271 } 272 } 273 Object[] result = new Object[3]; 274 result[0] = c; 275 result[1] = draftMailboxKey; 276 result[2] = trashMailboxKey; 277 return result; 278 } 279 280 @Override 281 protected void onPostExecute(Object[] results) { 282 if (results == null || isCancelled()) return; 283 Cursor cursor = (Cursor) results[0]; 284 mDraftMailboxKey = (Long) results[1]; 285 mTrashMailboxKey = (Long) results[2]; 286 287 if (cursor.isClosed()) return; 288 mListAdapter.changeCursor(cursor); 289 setListAdapter(mListAdapter); 290 updateMessageCount(); 291 } 292 } 293 294 private class MessageCountTask extends AsyncTask<Void, Void, int[]> { 295 296 @Override 297 protected int[] doInBackground(Void... params) { 298 int[] counts = new int[2]; 299 if (mDraftMailboxKey != -1) { 300 counts[0] = EmailContent.count(mActivity, Message.CONTENT_URI, 301 MESSAGE_MAILBOX_ID_SELECTION, 302 new String[] { String.valueOf(mDraftMailboxKey)}); 303 } else { 304 counts[0] = 0; 305 } 306 if (mTrashMailboxKey != -1) { 307 counts[1] = EmailContent.count(mActivity, Message.CONTENT_URI, 308 MESSAGE_MAILBOX_ID_SELECTION, 309 new String[] { String.valueOf(mTrashMailboxKey)}); 310 } else { 311 counts[1] = 0; 312 } 313 return counts; 314 } 315 316 @Override 317 protected void onPostExecute(int[] counts) { 318 if (counts == null || isCancelled()) { 319 return; 320 } 321 int countDraft = counts[0]; 322 int countTrash = counts[1]; 323 mListAdapter.setMessageCounts(countDraft, countTrash); 324 } 325 } 326 327 private void updateMessageCount() { 328 if (mAccountId == -1 || mListAdapter.getCursor() == null) { 329 return; 330 } 331 if (mMessageCountTask != null 332 && mMessageCountTask.getStatus() != MessageCountTask.Status.FINISHED) { 333 mMessageCountTask.cancel(true); 334 } 335 mMessageCountTask = (MessageCountTask) new MessageCountTask().execute(); 336 } 337} 338