MailboxFragmentAdapter.java revision d531dc3058c52b1e3ee2ee53eb8d63d6177d9f91
1/*
2 * Copyright (C) 2011 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.FolderProperties;
21import com.android.email.R;
22import com.android.email.data.ClosingMatrixCursor;
23import com.android.email.data.ThrottlingCursorLoader;
24import com.android.emailcommon.Logging;
25import com.android.emailcommon.provider.EmailContent;
26import com.android.emailcommon.provider.EmailContent.Account;
27import com.android.emailcommon.provider.EmailContent.AccountColumns;
28import com.android.emailcommon.provider.EmailContent.MailboxColumns;
29import com.android.emailcommon.provider.EmailContent.Message;
30import com.android.emailcommon.provider.Mailbox;
31import com.android.emailcommon.utility.Utility;
32
33import android.content.Context;
34import android.content.Loader;
35import android.database.Cursor;
36import android.database.MatrixCursor;
37import android.database.MatrixCursor.RowBuilder;
38import android.database.MergeCursor;
39import android.util.Log;
40import android.view.View;
41import android.view.ViewGroup;
42import android.widget.ImageView;
43import android.widget.TextView;
44
45/**
46 * Mailbox cursor adapter for the mailbox list fragment.
47 *
48 * A mailbox cursor may contain one of several different types of data. Currently, this
49 * adapter supports the following views:
50 * 1. The standard inbox, mailbox view
51 * 2. The combined mailbox view
52 * 3. Nested folder navigation
53 *
54 * TODO At a minimum, we should break out the loaders. They have no relation to the view code
55 * and only serve to confuse the user.
56 * TODO Determine if we actually need a separate adapter / view / loader for nested folder
57 * navigation. It's a little convoluted at the moment, but, still manageable.
58 */
59/*package*/ class MailboxFragmentAdapter extends MailboxesAdapter {
60    private static final String MAILBOX_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?" +
61            " AND " + MailboxColumns.ID + "=?";
62    public MailboxFragmentAdapter(Context context, Callback callback) {
63        super(context, callback);
64    }
65
66    @Override
67    public void bindView(View view, Context context, Cursor cursor) {
68        final boolean isAccount = isAccountRow(cursor);
69        final int type = cursor.getInt(COLUMN_TYPE);
70        final long id = cursor.getLong(COLUMN_ID);
71        final long accountId = cursor.getLong(COLUMN_ACCOUNT_ID);
72        final int flags = cursor.getInt(COLUMN_FLAGS);
73        final int rowType = cursor.getInt(COLUMN_ROW_TYPE);
74        final boolean hasVisibleChildren = (flags & Mailbox.FLAG_HAS_CHILDREN) != 0
75                && (flags & Mailbox.FLAG_CHILDREN_VISIBLE) != 0;
76
77        MailboxListItem listItem = (MailboxListItem)view;
78        listItem.mMailboxId = isAccountRow(cursor) ? Mailbox.NO_MAILBOX : id;
79        listItem.mMailboxType = type;
80        listItem.mAccountId = accountId;
81        listItem.mIsValidDropTarget = (id >= 0)
82                && !Utility.arrayContains(Mailbox.INVALID_DROP_TARGETS, type)
83                && (flags & Mailbox.FLAG_ACCEPTS_MOVED_MAIL) != 0;
84        listItem.mIsNavigable = hasVisibleChildren;
85
86        listItem.mAdapter = this;
87        // Set the background depending on whether we're in drag mode, the mailbox is a valid
88        // target, etc.
89        mCallback.onBind(listItem);
90
91        // Set mailbox name
92        final TextView nameView = (TextView) view.findViewById(R.id.mailbox_name);
93        nameView.setText(getDisplayName(context, cursor));
94        // Set count
95        final int count;
96        switch (getCountTypeForMailboxType(cursor)) {
97            case COUNT_TYPE_UNREAD:
98                count = cursor.getInt(COLUMN_UNREAD_COUNT);
99                break;
100            case COUNT_TYPE_TOTAL:
101                count = cursor.getInt(COLUMN_MESSAGE_COUNT);
102                break;
103            default: // no count
104                count = 0;
105                break;
106        }
107        final TextView countView = (TextView) view.findViewById(R.id.message_count);
108
109        // Set folder icon
110        final ImageView folderIcon = (ImageView) view.findViewById(R.id.folder_icon);
111        folderIcon.setImageDrawable(
112                FolderProperties.getInstance(context).getIcon(type, id, flags));
113
114        final ImageView mailboxExpandedIcon =
115                (ImageView) view.findViewById(R.id.folder_expanded_icon);
116        switch (rowType) {
117            case ROW_TYPE_SUBMAILBOX:
118                if (hasVisibleChildren) {
119                    mailboxExpandedIcon.setVisibility(View.VISIBLE);
120                    mailboxExpandedIcon.setImageResource(
121                            R.drawable.ic_mailbox_collapsed_holo_light);
122                } else {
123                    mailboxExpandedIcon.setVisibility(View.INVISIBLE);
124                    mailboxExpandedIcon.setImageDrawable(null);
125                }
126                folderIcon.setVisibility(View.INVISIBLE);
127                break;
128            case ROW_TYPE_CURMAILBOX:
129                mailboxExpandedIcon.setVisibility(View.GONE);
130                mailboxExpandedIcon.setImageDrawable(null);
131                folderIcon.setVisibility(View.GONE);
132                break;
133            case ROW_TYPE_MAILBOX:
134            default: // Includes ROW_TYPE_ACCOUNT
135                if (hasVisibleChildren) {
136                    mailboxExpandedIcon.setVisibility(View.VISIBLE);
137                    mailboxExpandedIcon.setImageResource(
138                            R.drawable.ic_mailbox_collapsed_holo_light);
139                } else {
140                    mailboxExpandedIcon.setVisibility(View.GONE);
141                    mailboxExpandedIcon.setImageDrawable(null);
142                }
143                folderIcon.setVisibility(View.VISIBLE);
144                break;
145        }
146
147        // If the unread count is zero, not to show countView.
148        if (count > 0) {
149            countView.setVisibility(View.VISIBLE);
150            countView.setText(Integer.toString(count));
151        } else {
152            countView.setVisibility(View.GONE);
153        }
154
155        final View chipView = view.findViewById(R.id.color_chip);
156        if (isAccount) {
157            chipView.setVisibility(View.VISIBLE);
158            chipView.setBackgroundColor(mResourceHelper.getAccountColor(id));
159        } else {
160            chipView.setVisibility(View.GONE);
161        }
162    }
163
164    @Override
165    public View newView(Context context, Cursor cursor, ViewGroup parent) {
166        return mInflater.inflate(R.layout.mailbox_list_item, parent, false);
167    }
168
169    /**
170     * Returns a cursor loader for the mailboxes of the given account. If <code>parentKey</code>
171     * refers to a valid mailbox ID [e.g. non-zero], restrict the loader to only those mailboxes
172     * contained by this parent mailbox.
173     */
174    public static Loader<Cursor> createLoader(Context context, long accountId, long parentKey) {
175        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
176            Log.d(Logging.LOG_TAG, "MailboxFragmentAdapter#createLoader accountId=" + accountId);
177        }
178        if (accountId != Account.ACCOUNT_ID_COMBINED_VIEW) {
179            return new MailboxFragmentLoader(context, accountId, parentKey);
180        } else {
181            return new CombinedMailboxLoader(context);
182        }
183    }
184
185    /**
186     * Adds a new row into the given cursor.
187     */
188    private static void addMailboxRow(MatrixCursor cursor, long mailboxId, String displayName,
189            int mailboxType, int unreadCount, int messageCount, int rowType, int flags,
190            long accountId) {
191        long listId = mailboxId;
192        if (mailboxId < 0) {
193            listId = Long.MAX_VALUE + mailboxId; // IDs for the list view must be positive
194        }
195        RowBuilder row = cursor.newRow();
196        row.add(listId);
197        row.add(mailboxId);
198        row.add(displayName);
199        row.add(mailboxType);
200        row.add(unreadCount);
201        row.add(messageCount);
202        row.add(rowType);
203        row.add(flags);
204        row.add(accountId);
205    }
206
207    private static void addCombinedMailboxRow(MatrixCursor cursor, long id, int mailboxType,
208            int count, boolean showAlways) {
209        if (id >= 0) {
210            throw new IllegalArgumentException(); // Must be QUERY_ALL_*, which are all negative
211        }
212        if (showAlways || (count > 0)) {
213            addMailboxRow(
214                    cursor, id, "", mailboxType, count, count, ROW_TYPE_MAILBOX, Mailbox.FLAG_NONE,
215                    Account.ACCOUNT_ID_COMBINED_VIEW);
216        }
217    }
218
219    /**
220     * Loader for mailboxes of an account.
221     */
222    private static class MailboxFragmentLoader extends ThrottlingCursorLoader {
223        private final Context mContext;
224        private final long mAccountId;
225        private final long mParentKey;
226
227        MailboxFragmentLoader(Context context, long accountId, long parentKey) {
228            super(context, Mailbox.CONTENT_URI,
229                    (parentKey != Mailbox.NO_MAILBOX)
230                            ? MailboxesAdapter.SUBMAILBOX_PROJECTION
231                            : MailboxesAdapter.PROJECTION,
232                    MAILBOX_SELECTION_WITH_PARENT,
233                    new String[] { Long.toString(accountId), Long.toString(parentKey) },
234                    MAILBOX_ORDER_BY);
235            mContext = context;
236            mAccountId = accountId;
237            mParentKey = parentKey;
238        }
239
240        @Override
241        public void onContentChanged() {
242            if (sEnableUpdate) {
243                super.onContentChanged();
244            }
245        }
246
247        @Override
248        public Cursor loadInBackground() {
249            final Cursor childMailboxCursor = super.loadInBackground();
250
251            // If we're not showing the top level mailboxes, add the "parent" mailbox.
252            if (mParentKey != Mailbox.NO_MAILBOX) {
253                final Cursor parentCursor = getContext().getContentResolver().query(
254                        Mailbox.CONTENT_URI, CURMAILBOX_PROJECTION, MAILBOX_SELECTION,
255                        new String[] { Long.toString(mAccountId), Long.toString(mParentKey) },
256                        null);
257                return Utility.CloseTraceCursorWrapper.get(new MergeCursor(
258                        new Cursor[] { parentCursor, childMailboxCursor }));
259            }
260
261            // Add "Starred", only if the account has at least one starred message.
262            // TODO It's currently "combined starred", but the plan is to make it per-account
263            // starred.
264            final int accountStarredCount = Message.getFavoriteMessageCount(mContext, mAccountId);
265            if (accountStarredCount > 0) {
266                final MatrixCursor starredCursor = new MatrixCursor(getProjection());
267                final int totalStarredCount = Message.getFavoriteMessageCount(mContext);
268                addCombinedMailboxRow(starredCursor, Mailbox.QUERY_ALL_FAVORITES, Mailbox.TYPE_MAIL,
269                        totalStarredCount, true);
270                return Utility.CloseTraceCursorWrapper.get(
271                        new MergeCursor(new Cursor[] { starredCursor, childMailboxCursor }));
272            }
273
274            return Utility.CloseTraceCursorWrapper.get(childMailboxCursor); // no starred message
275        }
276    }
277
278    /**
279     * Loader for mailboxes in "Combined view".
280     */
281    /*package*/ static class CombinedMailboxLoader extends ThrottlingCursorLoader {
282        private static final String[] ACCOUNT_PROJECTION = new String[] {
283                    EmailContent.RECORD_ID, AccountColumns.DISPLAY_NAME,
284                };
285        private static final int COLUMN_ACCOUND_ID = 0;
286        private static final int COLUMN_ACCOUNT_DISPLAY_NAME = 1;
287
288        private final Context mContext;
289
290        public CombinedMailboxLoader(Context context) {
291            super(context, Account.CONTENT_URI, ACCOUNT_PROJECTION, null, null, null);
292            mContext = context;
293        }
294
295        @Override
296        public Cursor loadInBackground() {
297            final Cursor accounts = super.loadInBackground();
298
299            // Build combined mailbox rows.
300            final MatrixCursor combinedWithAccounts = buildCombinedMailboxes(mContext, accounts);
301
302            // Add account rows.
303            accounts.moveToPosition(-1);
304            while (accounts.moveToNext()) {
305                final long accountId = accounts.getLong(COLUMN_ACCOUND_ID);
306                final String accountName = accounts.getString(COLUMN_ACCOUNT_DISPLAY_NAME);
307                final int unreadCount = Mailbox.getUnreadCountByAccountAndMailboxType(
308                        mContext, accountId, Mailbox.TYPE_INBOX);
309                addMailboxRow(combinedWithAccounts, accountId, accountName, Mailbox.TYPE_NONE,
310                        unreadCount, unreadCount, ROW_TYPE_ACCOUNT, Mailbox.FLAG_NONE,
311                        accountId);
312            }
313            return Utility.CloseTraceCursorWrapper.get(combinedWithAccounts);
314        }
315
316        /*package*/ static MatrixCursor buildCombinedMailboxes(Context context,
317                Cursor innerCursor) {
318            MatrixCursor cursor = new ClosingMatrixCursor(PROJECTION, innerCursor);
319            // Combined inbox -- show unread count
320            addCombinedMailboxRow(cursor, Mailbox.QUERY_ALL_INBOXES, Mailbox.TYPE_INBOX,
321                    Mailbox.getUnreadCountByMailboxType(context, Mailbox.TYPE_INBOX), true);
322
323            // Favorite (starred) -- show # of favorites
324            addCombinedMailboxRow(cursor, Mailbox.QUERY_ALL_FAVORITES, Mailbox.TYPE_MAIL,
325                    Message.getFavoriteMessageCount(context), false);
326
327            // Drafts -- show # of drafts
328            addCombinedMailboxRow(cursor, Mailbox.QUERY_ALL_DRAFTS, Mailbox.TYPE_DRAFTS,
329                    Mailbox.getMessageCountByMailboxType(context, Mailbox.TYPE_DRAFTS), false);
330
331            // Outbox -- # of outstanding messages
332            addCombinedMailboxRow(cursor, Mailbox.QUERY_ALL_OUTBOX, Mailbox.TYPE_OUTBOX,
333                    Mailbox.getMessageCountByMailboxType(context, Mailbox.TYPE_OUTBOX), false);
334
335            return cursor;
336        }
337    }
338}
339