MailboxListFragment.java revision 58843f0dc336983bde54307d7387dcfcfc461017
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 private final MyLoaderCallbacks mMyLoaderCallbacks = new MyLoaderCallbacks(); 62 63 private ListView mListView; 64 65 private boolean mOpenRequested; 66 private boolean mResumed; 67 68 private Utility.ListStateSaver mSavedListState; 69 70 /** 71 * Callback interface that owning activities must implement 72 */ 73 public interface Callback { 74 public void onMailboxSelected(long accountId, long mailboxId); 75 } 76 77 private static class EmptyCallback implements Callback { 78 public static final Callback INSTANCE = new EmptyCallback(); 79 @Override 80 public void onMailboxSelected(long accountId, long mailboxId) { 81 } 82 } 83 84 /** 85 * Called to do initial creation of a fragment. This is called after 86 * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}. 87 */ 88 @Override 89 public void onCreate(Bundle savedInstanceState) { 90 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 91 Log.d(Email.LOG_TAG, "MailboxListFragment onCreate"); 92 } 93 super.onCreate(savedInstanceState); 94 95 mActivity = getActivity(); 96 mListAdapter = new MailboxesAdapter(mActivity); 97 if (savedInstanceState != null) { 98 restoreInstanceState(savedInstanceState); 99 } 100 } 101 102 @Override 103 public void onActivityCreated(Bundle savedInstanceState) { 104 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 105 Log.d(Email.LOG_TAG, "MailboxListFragment onActivityCreated"); 106 } 107 super.onActivityCreated(savedInstanceState); 108 109 mListView = getListView(); 110 mListView.setOnItemClickListener(this); 111 mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 112 registerForContextMenu(mListView); 113 } 114 115 public void setCallback(Callback callback) { 116 mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback; 117 } 118 119 /** 120 * @param accountId the account we're looking at 121 */ 122 public void openMailboxes(long accountId) { 123 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 124 Log.d(Email.LOG_TAG, "MailboxListFragment openMailboxes"); 125 } 126 if (accountId == -1) { 127 throw new InvalidParameterException(); 128 } 129 if (mAccountId == accountId) { 130 return; 131 } 132 mOpenRequested = true; 133 mAccountId = accountId; 134 if (mResumed) { 135 startLoading(); 136 } 137 } 138 139 public void setSelectedMailbox(long mailboxId) { 140 mSelectedMailboxId = mailboxId; 141 if (mResumed) { 142 highlightSelectedMailbox(); 143 } 144 } 145 146 /** 147 * Called when the Fragment is visible to the user. 148 */ 149 @Override 150 public void onStart() { 151 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 152 Log.d(Email.LOG_TAG, "MailboxListFragment onStart"); 153 } 154 super.onStart(); 155 } 156 157 /** 158 * Called when the fragment is visible to the user and actively running. 159 */ 160 @Override 161 public void onResume() { 162 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 163 Log.d(Email.LOG_TAG, "MailboxListFragment onResume"); 164 } 165 super.onResume(); 166 mResumed = true; 167 168 // If we're recovering from the stopped state, we don't have to reload. 169 // (when mOpenRequested = false) 170 if (mAccountId != -1 && mOpenRequested) { 171 startLoading(); 172 } 173 } 174 175 @Override 176 public void onPause() { 177 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 178 Log.d(Email.LOG_TAG, "MailboxListFragment onPause"); 179 } 180 mResumed = false; 181 super.onPause(); 182 mSavedListState = new Utility.ListStateSaver(getListView()); 183 } 184 185 /** 186 * Called when the Fragment is no longer started. 187 */ 188 @Override 189 public void onStop() { 190 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 191 Log.d(Email.LOG_TAG, "MailboxListFragment onStop"); 192 } 193 super.onStop(); 194 } 195 196 /** 197 * Called when the fragment is no longer in use. 198 */ 199 @Override 200 public void onDestroy() { 201 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 202 Log.d(Email.LOG_TAG, "MailboxListFragment onDestroy"); 203 } 204 super.onDestroy(); 205 } 206 207 @Override 208 public void onSaveInstanceState(Bundle outState) { 209 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 210 Log.d(Email.LOG_TAG, "MailboxListFragment onSaveInstanceState"); 211 } 212 super.onSaveInstanceState(outState); 213 outState.putLong(BUNDLE_KEY_SELECTED_MAILBOX_ID, mSelectedMailboxId); 214 outState.putParcelable(BUNDLE_LIST_STATE, new Utility.ListStateSaver(getListView())); 215 } 216 217 private void restoreInstanceState(Bundle savedInstanceState) { 218 mSelectedMailboxId = savedInstanceState.getLong(BUNDLE_KEY_SELECTED_MAILBOX_ID); 219 mSavedListState = savedInstanceState.getParcelable(BUNDLE_LIST_STATE); 220 } 221 222 private void startLoading() { 223 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 224 Log.d(Email.LOG_TAG, "MailboxListFragment startLoading"); 225 } 226 mOpenRequested = false; 227 // Clear the list. (ListFragment will show the "Loading" animation) 228 setListShown(false); 229 230 // If we've already loaded for a different account, discard the previous result and 231 // start loading again. 232 // We don't want to use restartLoader(), because if the Loader is retained, we *do* want to 233 // reuse the previous result. 234 if ((mLastLoadedAccountId != -1) && (mLastLoadedAccountId != mAccountId)) { 235 getLoaderManager().stopLoader(LOADER_ID_MAILBOX_LIST); 236 } 237 getLoaderManager().initLoader(LOADER_ID_MAILBOX_LIST, null, mMyLoaderCallbacks); 238 } 239 240 private class MyLoaderCallbacks implements LoaderCallbacks<Cursor> { 241 @Override 242 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 243 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 244 Log.d(Email.LOG_TAG, "MailboxListFragment onCreateLoader"); 245 } 246 return MailboxesAdapter.createLoader(getActivity(), mAccountId, 247 MailboxesAdapter.MODE_NORMAL); 248 } 249 250 @Override 251 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { 252 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 253 Log.d(Email.LOG_TAG, "MailboxListFragment onLoadFinished"); 254 } 255 mLastLoadedAccountId = mAccountId; 256 257 // Save list view state (primarily scroll position) 258 final ListView lv = getListView(); 259 final Utility.ListStateSaver lss; 260 if (mSavedListState != null) { 261 lss = mSavedListState; 262 mSavedListState = null; 263 } else { 264 lss = new Utility.ListStateSaver(lv); 265 } 266 267 if (cursor.getCount() == 0) { 268 // If there's no row, don't set it to the ListView. 269 // Instead use setListShown(false) to make ListFragment show progress icon. 270 mListAdapter.changeCursor(null); 271 setListShown(false); 272 } else { 273 // Set the adapter. 274 mListAdapter.changeCursor(cursor); 275 setListAdapter(mListAdapter); 276 setListShown(true); 277 highlightSelectedMailbox(); 278 } 279 280 // Restore the state 281 lss.restore(lv); 282 } 283 } 284 285 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 286 mCallback.onMailboxSelected(mAccountId, id); 287 } 288 289 public void onRefresh() { 290 if (mAccountId != -1) { 291 RefreshManager.getInstance(getActivity()).refreshMailboxList(mAccountId); 292 } 293 } 294 295 /** 296 * Highlight the selected mailbox. 297 */ 298 private void highlightSelectedMailbox() { 299 if (mSelectedMailboxId == -1) { 300 // No mailbox selected 301 mListView.clearChoices(); 302 return; 303 } 304 final int count = mListView.getCount(); 305 for (int i = 0; i < count; i++) { 306 if (mListView.getItemIdAtPosition(i) == mSelectedMailboxId) { 307 mListView.setItemChecked(i, true); 308 mListView.smoothScrollToPosition(i); 309 break; 310 } 311 } 312 } 313} 314