MailboxListFragment.java revision 56034cab2022fe78ccbf4635617b1a54b4cc16b8
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.Email; 20import com.android.email.RefreshManager; 21import com.android.email.Utility; 22 23import android.app.Activity; 24import android.app.ListFragment; 25import android.app.LoaderManager.LoaderCallbacks; 26import android.content.Loader; 27import android.database.Cursor; 28import android.os.Bundle; 29import android.util.Log; 30import android.view.View; 31import android.widget.AdapterView; 32import android.widget.AdapterView.OnItemClickListener; 33import android.widget.ListView; 34 35import java.security.InvalidParameterException; 36 37/** 38 * This fragment presents a list of mailboxes for a given account. The "API" includes the 39 * following elements which must be provided by the host Activity. 40 * 41 * - call bindActivityInfo() to provide the account ID and set callbacks 42 * - provide callbacks for onOpen and onRefresh 43 * - pass-through implementations of onCreateContextMenu() and onContextItemSelected() (temporary) 44 * 45 * TODO Restoring ListView state -- don't do this when changing accounts 46 */ 47public class MailboxListFragment extends ListFragment implements OnItemClickListener { 48 private static final String BUNDLE_KEY_SELECTED_MAILBOX_ID 49 = "MailboxListFragment.state.selected_mailbox_id"; 50 private static final String BUNDLE_LIST_STATE = "MailboxListFragment.state.listState"; 51 private static final int LOADER_ID_MAILBOX_LIST = 1; 52 53 private long mLastLoadedAccountId = -1; 54 private long mAccountId = -1; 55 private long mSelectedMailboxId = -1; 56 57 // UI Support 58 private Activity mActivity; 59 private MailboxesAdapter mListAdapter; 60 private Callback mCallback = EmptyCallback.INSTANCE; 61 62 private ListView mListView; 63 64 private boolean mOpenRequested; 65 private boolean mResumed; 66 67 private Utility.ListStateSaver mSavedListState; 68 69 /** 70 * Callback interface that owning activities must implement 71 */ 72 public interface Callback { 73 public void onMailboxSelected(long accountId, long mailboxId); 74 } 75 76 private static class EmptyCallback implements Callback { 77 public static final Callback INSTANCE = new EmptyCallback(); 78 @Override 79 public void onMailboxSelected(long accountId, long mailboxId) { 80 } 81 } 82 83 /** 84 * Called to do initial creation of a fragment. This is called after 85 * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}. 86 */ 87 @Override 88 public void onCreate(Bundle savedInstanceState) { 89 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 90 Log.d(Email.LOG_TAG, "MailboxListFragment onCreate"); 91 } 92 super.onCreate(savedInstanceState); 93 94 mActivity = getActivity(); 95 mListAdapter = new MailboxesAdapter(mActivity, MailboxesAdapter.MODE_NORMAL); 96 if (savedInstanceState != null) { 97 restoreInstanceState(savedInstanceState); 98 } 99 } 100 101 @Override 102 public void onActivityCreated(Bundle savedInstanceState) { 103 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 104 Log.d(Email.LOG_TAG, "MailboxListFragment onActivityCreated"); 105 } 106 super.onActivityCreated(savedInstanceState); 107 108 mListView = getListView(); 109 mListView.setOnItemClickListener(this); 110 mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 111 registerForContextMenu(mListView); 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 (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 123 Log.d(Email.LOG_TAG, "MailboxListFragment openMailboxes"); 124 } 125 if (accountId == -1) { 126 throw new InvalidParameterException(); 127 } 128 if (mAccountId == accountId) { 129 return; 130 } 131 mOpenRequested = true; 132 mAccountId = accountId; 133 if (mResumed) { 134 startLoading(); 135 } 136 } 137 138 public void setSelectedMailbox(long mailboxId) { 139 mSelectedMailboxId = mailboxId; 140 if (mResumed) { 141 highlightSelectedMailbox(true); 142 } 143 } 144 145 /** 146 * Called when the Fragment is visible to the user. 147 */ 148 @Override 149 public void onStart() { 150 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 151 Log.d(Email.LOG_TAG, "MailboxListFragment onStart"); 152 } 153 super.onStart(); 154 } 155 156 /** 157 * Called when the fragment is visible to the user and actively running. 158 */ 159 @Override 160 public void onResume() { 161 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 162 Log.d(Email.LOG_TAG, "MailboxListFragment onResume"); 163 } 164 super.onResume(); 165 mResumed = true; 166 167 // If we're recovering from the stopped state, we don't have to reload. 168 // (when mOpenRequested = false) 169 if (mAccountId != -1 && mOpenRequested) { 170 startLoading(); 171 } 172 } 173 174 @Override 175 public void onPause() { 176 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 177 Log.d(Email.LOG_TAG, "MailboxListFragment onPause"); 178 } 179 mResumed = false; 180 super.onPause(); 181 mSavedListState = new Utility.ListStateSaver(getListView()); 182 } 183 184 /** 185 * Called when the Fragment is no longer started. 186 */ 187 @Override 188 public void onStop() { 189 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 190 Log.d(Email.LOG_TAG, "MailboxListFragment onStop"); 191 } 192 super.onStop(); 193 } 194 195 /** 196 * Called when the fragment is no longer in use. 197 */ 198 @Override 199 public void onDestroy() { 200 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 201 Log.d(Email.LOG_TAG, "MailboxListFragment onDestroy"); 202 } 203 super.onDestroy(); 204 } 205 206 @Override 207 public void onSaveInstanceState(Bundle outState) { 208 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 209 Log.d(Email.LOG_TAG, "MailboxListFragment onSaveInstanceState"); 210 } 211 super.onSaveInstanceState(outState); 212 outState.putLong(BUNDLE_KEY_SELECTED_MAILBOX_ID, mSelectedMailboxId); 213 outState.putParcelable(BUNDLE_LIST_STATE, new Utility.ListStateSaver(getListView())); 214 } 215 216 private void restoreInstanceState(Bundle savedInstanceState) { 217 mSelectedMailboxId = savedInstanceState.getLong(BUNDLE_KEY_SELECTED_MAILBOX_ID); 218 mSavedListState = savedInstanceState.getParcelable(BUNDLE_LIST_STATE); 219 } 220 221 private void startLoading() { 222 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 223 Log.d(Email.LOG_TAG, "MailboxListFragment startLoading"); 224 } 225 mOpenRequested = false; 226 // Clear the list. (ListFragment will show the "Loading" animation) 227 setListShown(false); 228 229 // If we've already loaded for a different account, discard the previous result and 230 // start loading again. 231 // We don't want to use restartLoader(), because if the Loader is retained, we *do* want to 232 // reuse the previous result. 233 // Also, when changing accounts, we don't preserve scroll position. 234 boolean accountChanging = false; 235 if ((mLastLoadedAccountId != -1) && (mLastLoadedAccountId != mAccountId)) { 236 accountChanging = true; 237 getLoaderManager().stopLoader(LOADER_ID_MAILBOX_LIST); 238 } 239 getLoaderManager().initLoader(LOADER_ID_MAILBOX_LIST, null, 240 new MailboxListLoaderCallbacks(accountChanging)); 241 } 242 243 private class MailboxListLoaderCallbacks implements LoaderCallbacks<Cursor> { 244 private boolean mAccountChanging; 245 246 public MailboxListLoaderCallbacks(boolean accountChanging) { 247 mAccountChanging = accountChanging; 248 } 249 250 @Override 251 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 252 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 253 Log.d(Email.LOG_TAG, "MailboxListFragment onCreateLoader"); 254 } 255 return MailboxesAdapter.createLoader(getActivity(), mAccountId, 256 MailboxesAdapter.MODE_NORMAL); 257 } 258 259 @Override 260 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { 261 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 262 Log.d(Email.LOG_TAG, "MailboxListFragment onLoadFinished"); 263 } 264 mLastLoadedAccountId = mAccountId; 265 266 // Save list view state (primarily scroll position) 267 final ListView lv = getListView(); 268 final Utility.ListStateSaver lss; 269 if (mAccountChanging) { 270 lss = null; // Don't preserve list state 271 } else if (mSavedListState != null) { 272 lss = mSavedListState; 273 mSavedListState = null; 274 } else { 275 lss = new Utility.ListStateSaver(lv); 276 } 277 278 if (cursor.getCount() == 0) { 279 // If there's no row, don't set it to the ListView. 280 // Instead use setListShown(false) to make ListFragment show progress icon. 281 mListAdapter.changeCursor(null); 282 setListShown(false); 283 } else { 284 // Set the adapter. 285 mListAdapter.changeCursor(cursor); 286 setListAdapter(mListAdapter); 287 setListShown(true); 288 289 // We want to make selection visible only when account is changing.. 290 // i.e. Refresh caused by content changed events shouldn't scroll the list. 291 highlightSelectedMailbox(mAccountChanging); 292 } 293 294 // Restore the state 295 if (lss != null) { 296 lss.restore(lv); 297 } 298 299 // Clear this for next reload triggered by content changed events. 300 mAccountChanging = false; 301 } 302 } 303 304 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 305 mCallback.onMailboxSelected(mAccountId, id); 306 } 307 308 public void onRefresh() { 309 if (mAccountId != -1) { 310 RefreshManager.getInstance(getActivity()).refreshMailboxList(mAccountId); 311 } 312 } 313 314 /** 315 * Highlight the selected mailbox. 316 */ 317 private void highlightSelectedMailbox(boolean ensureSelectionVisible) { 318 if (mSelectedMailboxId == -1) { 319 // No mailbox selected 320 mListView.clearChoices(); 321 return; 322 } 323 final int count = mListView.getCount(); 324 for (int i = 0; i < count; i++) { 325 if (mListView.getItemIdAtPosition(i) != mSelectedMailboxId) { 326 continue; 327 } 328 mListView.setItemChecked(i, true); 329 if (ensureSelectionVisible) { 330 Log.w(Email.LOG_TAG, "MailboxListFragment -- ensure visible"); 331 Utility.listViewSmoothScrollToPosition(getActivity(), mListView, i); 332 } 333 break; 334 } 335 } 336} 337