MessagesAdapter.java revision 019341af98ffe2dcd484bd0468c9858d9e7cd7a3
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.ResourceHelper;
21import com.android.email.data.ThrottlingCursorLoader;
22import com.android.emailcommon.Logging;
23import com.android.emailcommon.provider.EmailContent;
24import com.android.emailcommon.provider.EmailContent.Mailbox;
25import com.android.emailcommon.provider.EmailContent.Message;
26import com.android.emailcommon.provider.EmailContent.MessageColumns;
27import com.android.emailcommon.utility.TextUtilities;
28import com.android.emailcommon.utility.Utility;
29
30import android.content.Context;
31import android.content.Loader;
32import android.database.Cursor;
33import android.database.MatrixCursor;
34import android.os.Bundle;
35import android.util.Log;
36import android.view.View;
37import android.view.ViewGroup;
38import android.widget.CursorAdapter;
39
40import java.util.HashSet;
41import java.util.Set;
42
43
44/**
45 * This class implements the adapter for displaying messages based on cursors.
46 */
47/* package */ class MessagesAdapter extends CursorAdapter {
48    private static final String STATE_CHECKED_ITEMS =
49            "com.android.email.activity.MessagesAdapter.checkedItems";
50
51    /* package */ static final String[] MESSAGE_PROJECTION = new String[] {
52        EmailContent.RECORD_ID, MessageColumns.MAILBOX_KEY, MessageColumns.ACCOUNT_KEY,
53        MessageColumns.DISPLAY_NAME, MessageColumns.SUBJECT, MessageColumns.TIMESTAMP,
54        MessageColumns.FLAG_READ, MessageColumns.FLAG_FAVORITE, MessageColumns.FLAG_ATTACHMENT,
55        MessageColumns.FLAGS, MessageColumns.SNIPPET
56    };
57
58    public static final int COLUMN_ID = 0;
59    public static final int COLUMN_MAILBOX_KEY = 1;
60    public static final int COLUMN_ACCOUNT_KEY = 2;
61    public static final int COLUMN_DISPLAY_NAME = 3;
62    public static final int COLUMN_SUBJECT = 4;
63    public static final int COLUMN_DATE = 5;
64    public static final int COLUMN_READ = 6;
65    public static final int COLUMN_FAVORITE = 7;
66    public static final int COLUMN_ATTACHMENTS = 8;
67    public static final int COLUMN_FLAGS = 9;
68    public static final int COLUMN_SNIPPET = 10;
69
70    private final ResourceHelper mResourceHelper;
71
72    /** If true, show color chips. */
73    private boolean mShowColorChips;
74
75    /** If not null, the query represented by this group of messages */
76    private String mQuery;
77
78    /**
79     * Set of seleced message IDs.
80     */
81    private final HashSet<Long> mSelectedSet = new HashSet<Long>();
82
83    /**
84     * Callback from MessageListAdapter.  All methods are called on the UI thread.
85     */
86    public interface Callback {
87        /** Called when the use starts/unstars a message */
88        void onAdapterFavoriteChanged(MessageListItem itemView, boolean newFavorite);
89        /** Called when the user selects/unselects a message */
90        void onAdapterSelectedChanged(MessageListItem itemView, boolean newSelected,
91                int mSelectedCount);
92    }
93
94    private final Callback mCallback;
95
96    public MessagesAdapter(Context context, Callback callback) {
97        super(context.getApplicationContext(), null, 0 /* no auto requery */);
98        mResourceHelper = ResourceHelper.getInstance(context);
99        mCallback = callback;
100    }
101
102    public void onSaveInstanceState(Bundle outState) {
103        Set<Long> checkedset = getSelectedSet();
104        long[] checkedarray = new long[checkedset.size()];
105        int i = 0;
106        for (Long l : checkedset) {
107            checkedarray[i] = l;
108            i++;
109        }
110        outState.putLongArray(STATE_CHECKED_ITEMS, checkedarray);
111    }
112
113    public void loadState(Bundle savedInstanceState) {
114        Set<Long> checkedset = getSelectedSet();
115        for (long l: savedInstanceState.getLongArray(STATE_CHECKED_ITEMS)) {
116            checkedset.add(l);
117        }
118    }
119
120    /**
121     * Set true for combined mailboxes.
122     */
123    public void setShowColorChips(boolean show) {
124        mShowColorChips = show;
125    }
126
127    public void setQuery(String query) {
128        mQuery = query;
129    }
130
131    public Set<Long> getSelectedSet() {
132        return mSelectedSet;
133    }
134
135    public boolean isSelected(MessageListItem itemView) {
136        return mSelectedSet.contains(itemView.mMessageId);
137    }
138
139    @Override
140    public void bindView(View view, Context context, Cursor cursor) {
141        // Reset the view (in case it was recycled) and prepare for binding
142        MessageListItem itemView = (MessageListItem) view;
143        itemView.bindViewInit(this);
144
145        // Load the public fields in the view (for later use)
146        itemView.mMessageId = cursor.getLong(COLUMN_ID);
147        itemView.mMailboxId = cursor.getLong(COLUMN_MAILBOX_KEY);
148        final long accountId = cursor.getLong(COLUMN_ACCOUNT_KEY);
149        itemView.mAccountId = accountId;
150        itemView.mRead = cursor.getInt(COLUMN_READ) != 0;
151        itemView.mIsFavorite = cursor.getInt(COLUMN_FAVORITE) != 0;
152        itemView.mHasInvite =
153            (cursor.getInt(COLUMN_FLAGS) & Message.FLAG_INCOMING_MEETING_INVITE) != 0;
154        itemView.mHasAttachment = cursor.getInt(COLUMN_ATTACHMENTS) != 0;
155        itemView.mTimestamp = cursor.getLong(COLUMN_DATE);
156        itemView.mSender = cursor.getString(COLUMN_DISPLAY_NAME);
157        itemView.mSnippet = cursor.getString(COLUMN_SNIPPET);
158        itemView.mSubject = cursor.getString(COLUMN_SUBJECT);
159        itemView.mSnippetLineCount = MessageListItem.NEEDS_LAYOUT;
160        itemView.mColorChipPaint =
161            mShowColorChips ? mResourceHelper.getAccountColorPaint(accountId) : null;
162
163        if (mQuery != null && itemView.mSnippet != null) {
164            itemView.mSnippet =
165                TextUtilities.highlightTermsInText(cursor.getString(COLUMN_SNIPPET), mQuery);
166        }
167    }
168
169    @Override
170    public View newView(Context context, Cursor cursor, ViewGroup parent) {
171        MessageListItem item = new MessageListItem(context);
172        item.setVisibility(View.VISIBLE);
173        return item;
174    }
175
176    public void toggleSelected(MessageListItem itemView) {
177        updateSelected(itemView, !isSelected(itemView));
178    }
179
180    /**
181     * This is used as a callback from the list items, to set the selected state
182     *
183     * <p>Must be called on the UI thread.
184     *
185     * @param itemView the item being changed
186     * @param newSelected the new value of the selected flag (checkbox state)
187     */
188    private void updateSelected(MessageListItem itemView, boolean newSelected) {
189        if (newSelected) {
190            mSelectedSet.add(itemView.mMessageId);
191        } else {
192            mSelectedSet.remove(itemView.mMessageId);
193        }
194        if (mCallback != null) {
195            mCallback.onAdapterSelectedChanged(itemView, newSelected, mSelectedSet.size());
196        }
197    }
198
199    /**
200     * This is used as a callback from the list items, to set the favorite state
201     *
202     * <p>Must be called on the UI thread.
203     *
204     * @param itemView the item being changed
205     * @param newFavorite the new value of the favorite flag (star state)
206     */
207    public void updateFavorite(MessageListItem itemView, boolean newFavorite) {
208        changeFavoriteIcon(itemView, newFavorite);
209        if (mCallback != null) {
210            mCallback.onAdapterFavoriteChanged(itemView, newFavorite);
211        }
212    }
213
214    private void changeFavoriteIcon(MessageListItem view, boolean isFavorite) {
215        view.invalidate();
216    }
217
218    public static Loader<Cursor> createLoader(Context context, long mailboxId) {
219        if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
220            Log.d(Logging.LOG_TAG, "MessagesAdapter createLoader mailboxId=" + mailboxId);
221        }
222        return new MessagesCursorLoader(context, mailboxId);
223
224    }
225
226    static private class MessagesCursorLoader extends ThrottlingCursorLoader {
227        private final Context mContext;
228        private final long mMailboxId;
229
230        public MessagesCursorLoader(Context context, long mailboxId) {
231            // Initialize with no where clause.  We'll set it later.
232            super(context, EmailContent.Message.CONTENT_URI,
233                    MESSAGE_PROJECTION, null, null,
234                    EmailContent.MessageColumns.TIMESTAMP + " DESC");
235            mContext = context;
236            mMailboxId = mailboxId;
237        }
238
239        @Override
240        public Cursor loadInBackground() {
241            Cursor returnCursor;
242            Mailbox box = Mailbox.restoreMailboxWithId(mContext, mMailboxId);
243            // Only perform a load if the selected mailbox can hold messages
244            if ((box.mFlags & Mailbox.FLAG_HOLDS_MAIL) != 0) {
245                // Determine the where clause.  (Can't do this on the UI thread.)
246                setSelection(Utility.buildMailboxIdSelection(mContext, mMailboxId));
247                // Then do a query to get the cursor
248                returnCursor = super.loadInBackground();
249            } else {
250                // return an empty cursor
251                returnCursor = new MatrixCursor(getProjection());
252            }
253            return Utility.CloseTraceCursorWrapper.get(returnCursor);
254        }
255    }
256}
257