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