MailboxListFragment.java revision 37e21ab968e3944b10304a620c3b0ecfe5175668
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
40/**
41 * This fragment presents a list of mailboxes for a given account.  The "API" includes the
42 * following elements which must be provided by the host Activity.
43 *
44 *  - call bindActivityInfo() to provide the account ID and set callbacks
45 *  - provide callbacks for onOpen and onRefresh
46 *  - pass-through implementations of onCreateContextMenu() and onContextItemSelected() (temporary)
47 */
48public class MailboxListFragment extends ListFragment implements OnItemClickListener {
49
50    private static final String MAILBOX_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?" +
51            " AND " + MailboxColumns.TYPE + "<" + Mailbox.TYPE_NOT_EMAIL +
52            " AND " + MailboxColumns.FLAG_VISIBLE + "=1";
53    private static final String MESSAGE_MAILBOX_ID_SELECTION = MessageColumns.MAILBOX_KEY + "=?";
54
55    // Account & mailboxes access
56    private long mAccountId;
57    private LoadMailboxesTask mLoadMailboxesTask;
58    private MessageCountTask mMessageCountTask;
59    private long mDraftMailboxKey = -1;
60    private long mTrashMailboxKey = -1;
61
62    // UI Support
63    private Activity mActivity;
64    private MailboxesAdapter mListAdapter;
65    private Callback mCallback;
66
67    /**
68     * Callback interface that owning activities must implement
69     */
70    public interface Callback {
71        public void onRefresh(long accountId, long mailboxId);
72        public void onOpen(long accountId, long mailboxId);
73    }
74
75    /**
76     * Called to do initial creation of a fragment.  This is called after
77     * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}.
78     */
79    @Override
80    public void onCreate(Bundle savedInstanceState) {
81        super.onCreate(savedInstanceState);
82    }
83
84    @Override
85    public void onActivityCreated(Bundle savedInstanceState) {
86        super.onActivityCreated(savedInstanceState);
87
88        mActivity = getActivity();
89
90        ListView listView = getListView();
91        listView.setOnItemClickListener(this);
92        listView.setItemsCanFocus(false);
93        registerForContextMenu(listView);
94
95        mListAdapter = new MailboxesAdapter(mActivity);
96    }
97
98    /**
99     * Called by activity during onCreate() to bind additional information
100     * @param accountId the account we're looking at
101     * @param callback if non-null, UI clicks (e.g. refresh or open) will be delivered here
102     */
103    public void bindActivityInfo(long accountId, Callback callback) {
104        mAccountId = accountId;
105        mCallback = callback;
106    }
107
108    /**
109     * Called when the Fragment is visible to the user.
110     */
111    @Override
112    public void onStart() {
113        super.onStart();
114        mLoadMailboxesTask = new LoadMailboxesTask(mAccountId);
115        mLoadMailboxesTask.execute();
116    }
117
118    /**
119     * Called when the fragment is visible to the user and actively running.
120     */
121    @Override
122    public void onResume() {
123        super.onResume();
124        updateMessageCount();
125    }
126
127    /**
128     * Called when the Fragment is no longer started.
129     */
130    @Override
131    public void onStop() {
132        super.onStop();
133        Utility.cancelTaskInterrupt(mLoadMailboxesTask);
134        mLoadMailboxesTask = null;
135        Utility.cancelTaskInterrupt(mMessageCountTask);
136        mMessageCountTask = null;
137    }
138
139    /**
140     * Called when the fragment is no longer in use.
141     */
142   @Override
143   public void onDestroy() {
144        super.onDestroy();
145
146        mListAdapter.changeCursor(null);
147   }
148
149    /**
150     * This is called via the activity
151     * TODO This will be removed when possible
152     */
153    @Override
154    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo info) {
155        AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) info;
156        Cursor c = (Cursor) getListView().getItemAtPosition(menuInfo.position);
157        String folderName = Utility.FolderProperties.getInstance(mActivity)
158                .getDisplayName(Integer.valueOf(c.getString(mListAdapter.COLUMN_TYPE)));
159        if (folderName == null) {
160            folderName = c.getString(mListAdapter.COLUMN_DISPLAY_NAME);
161        }
162
163        menu.setHeaderTitle(folderName);
164        mActivity.getMenuInflater().inflate(R.menu.mailbox_list_context, menu);
165    }
166
167    /**
168     * This is called via the activity
169     * TODO This will be removed when possible
170     */
171    @Override
172    public boolean onContextItemSelected(MenuItem item) {
173        AdapterView.AdapterContextMenuInfo info =
174            (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
175
176        switch (item.getItemId()) {
177            case R.id.refresh:
178                if (mCallback != null) {
179                    mCallback.onRefresh(mAccountId, info.id);
180                }
181                return true;
182            case R.id.open:
183                if (mCallback != null) {
184                    mCallback.onOpen(mAccountId, info.id);
185                }
186                return true;
187        }
188        return false;
189    }
190
191    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
192        if (mCallback != null) {
193            mCallback.onOpen(mAccountId, id);
194        }
195    }
196
197    /**
198     * Async task for loading the mailboxes for a given account
199     */
200    private class LoadMailboxesTask extends AsyncTask<Void, Void, Object[]> {
201
202        private long mAccountKey;
203
204        /**
205         * Special constructor to cache some local info
206         */
207        public LoadMailboxesTask(long accountId) {
208            mAccountKey = accountId;
209        }
210
211        @Override
212        protected Object[] doInBackground(Void... params) {
213            long draftMailboxKey = -1;
214            long trashMailboxKey = -1;
215            Cursor c = mActivity.managedQuery(
216                    EmailContent.Mailbox.CONTENT_URI,
217                    mListAdapter.PROJECTION,
218                    MAILBOX_SELECTION,
219                    new String[] { String.valueOf(mAccountKey) },
220                    MailboxColumns.TYPE + "," + MailboxColumns.DISPLAY_NAME);
221            c.moveToPosition(-1);
222            while (c.moveToNext()) {
223                long mailboxId = c.getInt(mListAdapter.COLUMN_ID);
224                switch (c.getInt(mListAdapter.COLUMN_TYPE)) {
225                case Mailbox.TYPE_DRAFTS:
226                    draftMailboxKey = mailboxId;
227                    break;
228                case Mailbox.TYPE_TRASH:
229                    trashMailboxKey = mailboxId;
230                    break;
231                }
232            }
233            Object[] result = new Object[3];
234            result[0] = c;
235            result[1] = draftMailboxKey;
236            result[2] = trashMailboxKey;
237            return result;
238        }
239
240        @Override
241        protected void onPostExecute(Object[] results) {
242            if (results == null) return;
243            Cursor cursor = (Cursor) results[0];
244            mDraftMailboxKey = (Long) results[1];
245            mTrashMailboxKey = (Long) results[2];
246
247            if (cursor.isClosed()) return;
248            mListAdapter.changeCursor(cursor);
249            setListAdapter(mListAdapter);
250            updateMessageCount();
251        }
252    }
253
254    private class MessageCountTask extends AsyncTask<Void, Void, int[]> {
255
256        @Override
257        protected int[] doInBackground(Void... params) {
258            int[] counts = new int[2];
259            if (mDraftMailboxKey != -1) {
260                counts[0] = EmailContent.count(mActivity, Message.CONTENT_URI,
261                        MESSAGE_MAILBOX_ID_SELECTION,
262                        new String[] { String.valueOf(mDraftMailboxKey)});
263            } else {
264                counts[0] = 0;
265            }
266            if (mTrashMailboxKey != -1) {
267                counts[1] = EmailContent.count(mActivity, Message.CONTENT_URI,
268                        MESSAGE_MAILBOX_ID_SELECTION,
269                        new String[] { String.valueOf(mTrashMailboxKey)});
270            } else {
271                counts[1] = 0;
272            }
273            return counts;
274        }
275
276        @Override
277        protected void onPostExecute(int[] counts) {
278            if (counts == null) {
279                return;
280            }
281            int countDraft = counts[0];
282            int countTrash = counts[1];
283            mListAdapter.setMessageCounts(countDraft, countTrash);
284        }
285    }
286
287    private void updateMessageCount() {
288        if (mAccountId == -1 || mListAdapter.getCursor() == null) {
289            return;
290        }
291        if (mMessageCountTask != null
292                && mMessageCountTask.getStatus() != MessageCountTask.Status.FINISHED) {
293            mMessageCountTask.cancel(true);
294        }
295        mMessageCountTask = (MessageCountTask) new MessageCountTask().execute();
296    }
297}
298