MailboxListFragment.java revision 62f9c4d2803382f89cf8a19ed12b53b639d547fe
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_ACCOUNT_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_ACCOUNT_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_ACCOUNT_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            // Set the adapter.
268            mListAdapter.changeCursor(cursor);
269            setListAdapter(mListAdapter);
270            setListShown(true);
271            highlightSelectedMailbox();
272
273            // Restore the state
274            lss.restore(lv);
275        }
276    }
277
278    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
279        mCallback.onMailboxSelected(mAccountId, id);
280    }
281
282    public void onRefresh() {
283        if (mAccountId != -1) {
284            RefreshManager.getInstance(getActivity()).refreshMailboxList(mAccountId);
285        }
286    }
287
288    /**
289     * Highlight the selected mailbox.
290     */
291    private void highlightSelectedMailbox() {
292        if (mSelectedMailboxId == -1) {
293            // No mailbox selected
294            mListView.clearChoices();
295            return;
296        }
297        final int count = mListView.getCount();
298        for (int i = 0; i < count; i++) {
299            if (mListView.getItemIdAtPosition(i) == mSelectedMailboxId) {
300                mListView.setItemChecked(i, true);
301                break;
302            }
303        }
304    }
305}
306