MessageListAdapter.java revision 161375d2c4eff31add607fc0befa5c781c6fd7f1
1/*
2 * Copyright (C) 2008 Esmertec AG.
3 * Copyright (C) 2008 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.android.mms.ui;
19
20import com.android.mms.R;
21import com.google.android.mms.MmsException;
22
23import android.content.Context;
24import android.database.Cursor;
25import android.os.Handler;
26import android.provider.BaseColumns;
27import android.provider.Telephony.Mms;
28import android.provider.Telephony.MmsSms;
29import android.provider.Telephony.Sms;
30import android.provider.Telephony.MmsSms.PendingMessages;
31import android.provider.Telephony.Sms.Conversations;
32import android.util.Config;
33import android.util.Log;
34import android.view.LayoutInflater;
35import android.view.View;
36import android.view.ViewGroup;
37import android.widget.CursorAdapter;
38import android.widget.ListView;
39
40import java.util.LinkedHashMap;
41import java.util.Map;
42
43/**
44 * The back-end data adapter of a message list.
45 */
46public class MessageListAdapter extends CursorAdapter {
47    private static final String TAG = "MessageListAdapter";
48    private static final boolean DEBUG = false;
49    private static final boolean LOCAL_LOGV = Config.LOGV && DEBUG;
50
51    static final String[] PROJECTION = new String[] {
52        // TODO: should move this symbol into android.provider.Telephony.
53        MmsSms.TYPE_DISCRIMINATOR_COLUMN,
54        BaseColumns._ID,
55        Conversations.THREAD_ID,
56        // For SMS
57        Sms.ADDRESS,
58        Sms.BODY,
59        Sms.DATE,
60        Sms.READ,
61        Sms.TYPE,
62        Sms.STATUS,
63        Sms.LOCKED,
64        Sms.ERROR_CODE,
65        // For MMS
66        Mms.SUBJECT,
67        Mms.SUBJECT_CHARSET,
68        Mms.DATE,
69        Mms.READ,
70        Mms.MESSAGE_TYPE,
71        Mms.MESSAGE_BOX,
72        Mms.DELIVERY_REPORT,
73        Mms.READ_REPORT,
74        PendingMessages.ERROR_TYPE,
75        Mms.LOCKED
76    };
77
78    // The indexes of the default columns which must be consistent
79    // with above PROJECTION.
80    static final int COLUMN_MSG_TYPE            = 0;
81    static final int COLUMN_ID                  = 1;
82    static final int COLUMN_THREAD_ID           = 2;
83    static final int COLUMN_SMS_ADDRESS         = 3;
84    static final int COLUMN_SMS_BODY            = 4;
85    static final int COLUMN_SMS_DATE            = 5;
86    static final int COLUMN_SMS_READ            = 6;
87    static final int COLUMN_SMS_TYPE            = 7;
88    static final int COLUMN_SMS_STATUS          = 8;
89    static final int COLUMN_SMS_LOCKED          = 9;
90    static final int COLUMN_SMS_ERROR_CODE      = 10;
91    static final int COLUMN_MMS_SUBJECT         = 11;
92    static final int COLUMN_MMS_SUBJECT_CHARSET = 12;
93    static final int COLUMN_MMS_DATE            = 13;
94    static final int COLUMN_MMS_READ            = 14;
95    static final int COLUMN_MMS_MESSAGE_TYPE    = 15;
96    static final int COLUMN_MMS_MESSAGE_BOX     = 16;
97    static final int COLUMN_MMS_DELIVERY_REPORT = 17;
98    static final int COLUMN_MMS_READ_REPORT     = 18;
99    static final int COLUMN_MMS_ERROR_TYPE      = 19;
100    static final int COLUMN_MMS_LOCKED          = 20;
101
102    private static final int CACHE_SIZE         = 50;
103
104    protected LayoutInflater mInflater;
105    private final ListView mListView;
106    private final LinkedHashMap<Long, MessageItem> mMessageItemCache;
107    private final ColumnsMap mColumnsMap;
108    private OnDataSetChangedListener mOnDataSetChangedListener;
109    private Handler mMsgListItemHandler;
110    private String mHighlight;
111
112    public MessageListAdapter(
113            Context context, Cursor c, ListView listView,
114            boolean useDefaultColumnsMap, String highlight, boolean autoRequery) {
115        super(context, c, autoRequery /* auto-requery */);
116        mHighlight = highlight != null ? highlight.toLowerCase() : null;
117
118        mInflater = (LayoutInflater) context.getSystemService(
119                Context.LAYOUT_INFLATER_SERVICE);
120        mListView = listView;
121        mMessageItemCache = new LinkedHashMap<Long, MessageItem>(
122                    10, 1.0f, true) {
123            @Override
124            protected boolean removeEldestEntry(Map.Entry eldest) {
125                return size() > CACHE_SIZE;
126            }
127        };
128
129        if (useDefaultColumnsMap) {
130            mColumnsMap = new ColumnsMap();
131        } else {
132            mColumnsMap = new ColumnsMap(c);
133        }
134    }
135
136    @Override
137    public void bindView(View view, Context context, Cursor cursor) {
138        if (view instanceof MessageListItem) {
139            String type = cursor.getString(mColumnsMap.mColumnMsgType);
140            long msgId = cursor.getLong(mColumnsMap.mColumnMsgId);
141
142            MessageItem msgItem = getCachedMessageItem(type, msgId, cursor);
143            if (msgItem != null) {
144                ((MessageListItem) view).bind(msgItem);
145                ((MessageListItem) view).setMsgListItemHandler(mMsgListItemHandler);
146            }
147        }
148    }
149
150    public interface OnDataSetChangedListener {
151        void onDataSetChanged(MessageListAdapter adapter);
152        void onContentChanged(MessageListAdapter adapter);
153    }
154
155    public void setOnDataSetChangedListener(OnDataSetChangedListener l) {
156        mOnDataSetChangedListener = l;
157    }
158
159    public void setMsgListItemHandler(Handler handler) {
160        mMsgListItemHandler = handler;
161    }
162
163    @Override
164    public void notifyDataSetChanged() {
165        super.notifyDataSetChanged();
166        if (LOCAL_LOGV) {
167            Log.v(TAG, "MessageListAdapter.notifyDataSetChanged().");
168        }
169
170        mListView.setSelection(mListView.getCount());
171        mMessageItemCache.clear();
172
173        if (mOnDataSetChangedListener != null) {
174            mOnDataSetChangedListener.onDataSetChanged(this);
175        }
176    }
177
178    @Override
179    protected void onContentChanged() {
180        if (mAutoRequery) {
181            super.onContentChanged();
182            return;
183        }
184        if (mCursor != null && !mCursor.isClosed()) {
185            if (mOnDataSetChangedListener != null) {
186                mOnDataSetChangedListener.onContentChanged(this);
187            }
188        }
189    }
190
191    @Override
192    public View newView(Context context, Cursor cursor, ViewGroup parent) {
193        return mInflater.inflate(R.layout.message_list_item, parent, false);
194    }
195
196    public MessageItem getCachedMessageItem(String type, long msgId, Cursor c) {
197        MessageItem item = mMessageItemCache.get(getKey(type, msgId));
198        if (item == null) {
199            try {
200                item = new MessageItem(mContext, type, c, mColumnsMap, mHighlight);
201                mMessageItemCache.put(getKey(item.mType, item.mMsgId), item);
202            } catch (MmsException e) {
203                Log.e(TAG, e.getMessage());
204            }
205        }
206        return item;
207    }
208
209    private static long getKey(String type, long id) {
210        if (type.equals("mms")) {
211            return -id;
212        } else {
213            return id;
214        }
215    }
216
217    public static class ColumnsMap {
218        public int mColumnMsgType;
219        public int mColumnMsgId;
220        public int mColumnSmsAddress;
221        public int mColumnSmsBody;
222        public int mColumnSmsDate;
223        public int mColumnSmsRead;
224        public int mColumnSmsType;
225        public int mColumnSmsStatus;
226        public int mColumnSmsLocked;
227        public int mColumnSmsErrorCode;
228        public int mColumnMmsSubject;
229        public int mColumnMmsSubjectCharset;
230        public int mColumnMmsDate;
231        public int mColumnMmsRead;
232        public int mColumnMmsMessageType;
233        public int mColumnMmsMessageBox;
234        public int mColumnMmsDeliveryReport;
235        public int mColumnMmsReadReport;
236        public int mColumnMmsErrorType;
237        public int mColumnMmsLocked;
238
239        public ColumnsMap() {
240            mColumnMsgType            = COLUMN_MSG_TYPE;
241            mColumnMsgId              = COLUMN_ID;
242            mColumnSmsAddress         = COLUMN_SMS_ADDRESS;
243            mColumnSmsBody            = COLUMN_SMS_BODY;
244            mColumnSmsDate            = COLUMN_SMS_DATE;
245            mColumnSmsType            = COLUMN_SMS_TYPE;
246            mColumnSmsStatus          = COLUMN_SMS_STATUS;
247            mColumnSmsLocked          = COLUMN_SMS_LOCKED;
248            mColumnSmsErrorCode       = COLUMN_SMS_ERROR_CODE;
249            mColumnMmsSubject         = COLUMN_MMS_SUBJECT;
250            mColumnMmsSubjectCharset  = COLUMN_MMS_SUBJECT_CHARSET;
251            mColumnMmsMessageType     = COLUMN_MMS_MESSAGE_TYPE;
252            mColumnMmsMessageBox      = COLUMN_MMS_MESSAGE_BOX;
253            mColumnMmsDeliveryReport  = COLUMN_MMS_DELIVERY_REPORT;
254            mColumnMmsReadReport      = COLUMN_MMS_READ_REPORT;
255            mColumnMmsErrorType       = COLUMN_MMS_ERROR_TYPE;
256            mColumnMmsLocked          = COLUMN_MMS_LOCKED;
257        }
258
259        public ColumnsMap(Cursor cursor) {
260            // Ignore all 'not found' exceptions since the custom columns
261            // may be just a subset of the default columns.
262            try {
263                mColumnMsgType = cursor.getColumnIndexOrThrow(
264                        MmsSms.TYPE_DISCRIMINATOR_COLUMN);
265            } catch (IllegalArgumentException e) {
266                Log.w("colsMap", e.getMessage());
267            }
268
269            try {
270                mColumnMsgId = cursor.getColumnIndexOrThrow(BaseColumns._ID);
271            } catch (IllegalArgumentException e) {
272                Log.w("colsMap", e.getMessage());
273            }
274
275            try {
276                mColumnSmsAddress = cursor.getColumnIndexOrThrow(Sms.ADDRESS);
277            } catch (IllegalArgumentException e) {
278                Log.w("colsMap", e.getMessage());
279            }
280
281            try {
282                mColumnSmsBody = cursor.getColumnIndexOrThrow(Sms.BODY);
283            } catch (IllegalArgumentException e) {
284                Log.w("colsMap", e.getMessage());
285            }
286
287            try {
288                mColumnSmsDate = cursor.getColumnIndexOrThrow(Sms.DATE);
289            } catch (IllegalArgumentException e) {
290                Log.w("colsMap", e.getMessage());
291            }
292
293            try {
294                mColumnSmsType = cursor.getColumnIndexOrThrow(Sms.TYPE);
295            } catch (IllegalArgumentException e) {
296                Log.w("colsMap", e.getMessage());
297            }
298
299            try {
300                mColumnSmsStatus = cursor.getColumnIndexOrThrow(Sms.STATUS);
301            } catch (IllegalArgumentException e) {
302                Log.w("colsMap", e.getMessage());
303            }
304
305            try {
306                mColumnSmsLocked = cursor.getColumnIndexOrThrow(Sms.LOCKED);
307            } catch (IllegalArgumentException e) {
308                Log.w("colsMap", e.getMessage());
309            }
310
311            try {
312                mColumnSmsErrorCode = cursor.getColumnIndexOrThrow(Sms.ERROR_CODE);
313            } catch (IllegalArgumentException e) {
314                Log.w("colsMap", e.getMessage());
315            }
316
317            try {
318                mColumnMmsSubject = cursor.getColumnIndexOrThrow(Mms.SUBJECT);
319            } catch (IllegalArgumentException e) {
320                Log.w("colsMap", e.getMessage());
321            }
322
323            try {
324                mColumnMmsSubjectCharset = cursor.getColumnIndexOrThrow(Mms.SUBJECT_CHARSET);
325            } catch (IllegalArgumentException e) {
326                Log.w("colsMap", e.getMessage());
327            }
328
329            try {
330                mColumnMmsMessageType = cursor.getColumnIndexOrThrow(Mms.MESSAGE_TYPE);
331            } catch (IllegalArgumentException e) {
332                Log.w("colsMap", e.getMessage());
333            }
334
335            try {
336                mColumnMmsMessageBox = cursor.getColumnIndexOrThrow(Mms.MESSAGE_BOX);
337            } catch (IllegalArgumentException e) {
338                Log.w("colsMap", e.getMessage());
339            }
340
341            try {
342                mColumnMmsDeliveryReport = cursor.getColumnIndexOrThrow(Mms.DELIVERY_REPORT);
343            } catch (IllegalArgumentException e) {
344                Log.w("colsMap", e.getMessage());
345            }
346
347            try {
348                mColumnMmsReadReport = cursor.getColumnIndexOrThrow(Mms.READ_REPORT);
349            } catch (IllegalArgumentException e) {
350                Log.w("colsMap", e.getMessage());
351            }
352
353            try {
354                mColumnMmsErrorType = cursor.getColumnIndexOrThrow(PendingMessages.ERROR_TYPE);
355            } catch (IllegalArgumentException e) {
356                Log.w("colsMap", e.getMessage());
357            }
358
359            try {
360                mColumnMmsLocked = cursor.getColumnIndexOrThrow(Mms.LOCKED);
361            } catch (IllegalArgumentException e) {
362                Log.w("colsMap", e.getMessage());
363            }
364        }
365    }
366}
367