MessageListAdapter.java revision 331864544ec51ba6807fc5471cc6d537b7fef198
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        // For MMS
65        Mms.SUBJECT,
66        Mms.SUBJECT_CHARSET,
67        Mms.DATE,
68        Mms.READ,
69        Mms.MESSAGE_TYPE,
70        Mms.MESSAGE_BOX,
71        Mms.DELIVERY_REPORT,
72        Mms.READ_REPORT,
73        PendingMessages.ERROR_TYPE,
74        Mms.LOCKED
75    };
76
77    // The indexes of the default columns which must be consistent
78    // with above PROJECTION.
79    static final int COLUMN_MSG_TYPE            = 0;
80    static final int COLUMN_ID                  = 1;
81    static final int COLUMN_THREAD_ID           = 2;
82    static final int COLUMN_SMS_ADDRESS         = 3;
83    static final int COLUMN_SMS_BODY            = 4;
84    static final int COLUMN_SMS_DATE            = 5;
85    static final int COLUMN_SMS_READ            = 6;
86    static final int COLUMN_SMS_TYPE            = 7;
87    static final int COLUMN_SMS_STATUS          = 8;
88    static final int COLUMN_SMS_LOCKED          = 9;
89    static final int COLUMN_MMS_SUBJECT         = 10;
90    static final int COLUMN_MMS_SUBJECT_CHARSET = 11;
91    static final int COLUMN_MMS_DATE            = 12;
92    static final int COLUMN_MMS_READ            = 13;
93    static final int COLUMN_MMS_MESSAGE_TYPE    = 14;
94    static final int COLUMN_MMS_MESSAGE_BOX     = 15;
95    static final int COLUMN_MMS_DELIVERY_REPORT = 16;
96    static final int COLUMN_MMS_READ_REPORT     = 17;
97    static final int COLUMN_MMS_ERROR_TYPE      = 18;
98    static final int COLUMN_MMS_LOCKED          = 19;
99
100    private static final int CACHE_SIZE         = 50;
101
102    protected LayoutInflater mInflater;
103    private final ListView mListView;
104    private final LinkedHashMap<Long, MessageItem> mMessageItemCache;
105    private final ColumnsMap mColumnsMap;
106    private OnDataSetChangedListener mOnDataSetChangedListener;
107    private Handler mMsgListItemHandler;
108    private String mHighlight;
109
110    public MessageListAdapter(
111            Context context, Cursor c, ListView listView,
112            boolean useDefaultColumnsMap, String highlight) {
113        super(context, c);
114        mHighlight = highlight != null ? highlight.toLowerCase() : null;
115
116        mInflater = (LayoutInflater) context.getSystemService(
117                Context.LAYOUT_INFLATER_SERVICE);
118        mListView = listView;
119        mMessageItemCache = new LinkedHashMap<Long, MessageItem>(
120                    10, 1.0f, true) {
121            @Override
122            protected boolean removeEldestEntry(Map.Entry eldest) {
123                return size() > CACHE_SIZE;
124            }
125        };
126
127        if (useDefaultColumnsMap) {
128            mColumnsMap = new ColumnsMap();
129        } else {
130            mColumnsMap = new ColumnsMap(c);
131        }
132    }
133
134    @Override
135    public void bindView(View view, Context context, Cursor cursor) {
136        if (view instanceof MessageListItem) {
137            String type = cursor.getString(mColumnsMap.mColumnMsgType);
138            long msgId = cursor.getLong(mColumnsMap.mColumnMsgId);
139
140            MessageItem msgItem = getCachedMessageItem(type, msgId, cursor);
141            if (msgItem != null) {
142                ((MessageListItem) view).bind(msgItem);
143                ((MessageListItem) view).setMsgListItemHandler(mMsgListItemHandler);
144            }
145        }
146    }
147
148    public interface OnDataSetChangedListener {
149        void onDataSetChanged(MessageListAdapter adapter);
150    }
151
152    public void setOnDataSetChangedListener(OnDataSetChangedListener l) {
153        mOnDataSetChangedListener = l;
154    }
155
156    public void setMsgListItemHandler(Handler handler) {
157        mMsgListItemHandler = handler;
158    }
159
160    @Override
161    public void notifyDataSetChanged() {
162        super.notifyDataSetChanged();
163        if (LOCAL_LOGV) {
164            Log.v(TAG, "MessageListAdapter.notifyDataSetChanged().");
165        }
166
167        mListView.setSelection(mListView.getCount());
168        mMessageItemCache.clear();
169
170        if (mOnDataSetChangedListener != null) {
171            mOnDataSetChangedListener.onDataSetChanged(this);
172        }
173    }
174
175    @Override
176    public View newView(Context context, Cursor cursor, ViewGroup parent) {
177        return mInflater.inflate(R.layout.message_list_item, parent, false);
178    }
179
180    public MessageItem getCachedMessageItem(String type, long msgId, Cursor c) {
181        MessageItem item = mMessageItemCache.get(getKey(type, msgId));
182        if (item == null) {
183            try {
184                item = new MessageItem(mContext, type, c, mColumnsMap, mHighlight);
185                mMessageItemCache.put(getKey(item.mType, item.mMsgId), item);
186            } catch (MmsException e) {
187                Log.e(TAG, e.getMessage());
188            }
189        }
190        return item;
191    }
192
193    private static long getKey(String type, long id) {
194        if (type.equals("mms")) {
195            return -id;
196        } else {
197            return id;
198        }
199    }
200
201    public static class ColumnsMap {
202        public int mColumnMsgType;
203        public int mColumnMsgId;
204        public int mColumnSmsAddress;
205        public int mColumnSmsBody;
206        public int mColumnSmsDate;
207        public int mColumnSmsRead;
208        public int mColumnSmsType;
209        public int mColumnSmsStatus;
210        public int mColumnSmsLocked;
211        public int mColumnMmsSubject;
212        public int mColumnMmsSubjectCharset;
213        public int mColumnMmsDate;
214        public int mColumnMmsRead;
215        public int mColumnMmsMessageType;
216        public int mColumnMmsMessageBox;
217        public int mColumnMmsDeliveryReport;
218        public int mColumnMmsReadReport;
219        public int mColumnMmsErrorType;
220        public int mColumnMmsLocked;
221
222        public ColumnsMap() {
223            mColumnMsgType            = COLUMN_MSG_TYPE;
224            mColumnMsgId              = COLUMN_ID;
225            mColumnSmsAddress         = COLUMN_SMS_ADDRESS;
226            mColumnSmsBody            = COLUMN_SMS_BODY;
227            mColumnSmsDate            = COLUMN_SMS_DATE;
228            mColumnSmsType            = COLUMN_SMS_TYPE;
229            mColumnSmsStatus          = COLUMN_SMS_STATUS;
230            mColumnSmsLocked          = COLUMN_SMS_LOCKED;
231            mColumnMmsSubject         = COLUMN_MMS_SUBJECT;
232            mColumnMmsSubjectCharset  = COLUMN_MMS_SUBJECT_CHARSET;
233            mColumnMmsMessageType     = COLUMN_MMS_MESSAGE_TYPE;
234            mColumnMmsMessageBox      = COLUMN_MMS_MESSAGE_BOX;
235            mColumnMmsDeliveryReport  = COLUMN_MMS_DELIVERY_REPORT;
236            mColumnMmsReadReport      = COLUMN_MMS_READ_REPORT;
237            mColumnMmsErrorType       = COLUMN_MMS_ERROR_TYPE;
238            mColumnMmsLocked          = COLUMN_MMS_LOCKED;
239        }
240
241        public ColumnsMap(Cursor cursor) {
242            // Ignore all 'not found' exceptions since the custom columns
243            // may be just a subset of the default columns.
244            try {
245                mColumnMsgType = cursor.getColumnIndexOrThrow(
246                        MmsSms.TYPE_DISCRIMINATOR_COLUMN);
247            } catch (IllegalArgumentException e) {
248                Log.w("colsMap", e.getMessage());
249            }
250
251            try {
252                mColumnMsgId = cursor.getColumnIndexOrThrow(BaseColumns._ID);
253            } catch (IllegalArgumentException e) {
254                Log.w("colsMap", e.getMessage());
255            }
256
257            try {
258                mColumnSmsAddress = cursor.getColumnIndexOrThrow(Sms.ADDRESS);
259            } catch (IllegalArgumentException e) {
260                Log.w("colsMap", e.getMessage());
261            }
262
263            try {
264                mColumnSmsBody = cursor.getColumnIndexOrThrow(Sms.BODY);
265            } catch (IllegalArgumentException e) {
266                Log.w("colsMap", e.getMessage());
267            }
268
269            try {
270                mColumnSmsDate = cursor.getColumnIndexOrThrow(Sms.DATE);
271            } catch (IllegalArgumentException e) {
272                Log.w("colsMap", e.getMessage());
273            }
274
275            try {
276                mColumnSmsType = cursor.getColumnIndexOrThrow(Sms.TYPE);
277            } catch (IllegalArgumentException e) {
278                Log.w("colsMap", e.getMessage());
279            }
280
281            try {
282                mColumnSmsStatus = cursor.getColumnIndexOrThrow(Sms.STATUS);
283            } catch (IllegalArgumentException e) {
284                Log.w("colsMap", e.getMessage());
285            }
286
287            try {
288                mColumnSmsLocked = cursor.getColumnIndexOrThrow(Sms.LOCKED);
289            } catch (IllegalArgumentException e) {
290                Log.w("colsMap", e.getMessage());
291            }
292
293            try {
294                mColumnMmsSubject = cursor.getColumnIndexOrThrow(Mms.SUBJECT);
295            } catch (IllegalArgumentException e) {
296                Log.w("colsMap", e.getMessage());
297            }
298
299            try {
300                mColumnMmsSubjectCharset = cursor.getColumnIndexOrThrow(Mms.SUBJECT_CHARSET);
301            } catch (IllegalArgumentException e) {
302                Log.w("colsMap", e.getMessage());
303            }
304
305            try {
306                mColumnMmsMessageType = cursor.getColumnIndexOrThrow(Mms.MESSAGE_TYPE);
307            } catch (IllegalArgumentException e) {
308                Log.w("colsMap", e.getMessage());
309            }
310
311            try {
312                mColumnMmsMessageBox = cursor.getColumnIndexOrThrow(Mms.MESSAGE_BOX);
313            } catch (IllegalArgumentException e) {
314                Log.w("colsMap", e.getMessage());
315            }
316
317            try {
318                mColumnMmsDeliveryReport = cursor.getColumnIndexOrThrow(Mms.DELIVERY_REPORT);
319            } catch (IllegalArgumentException e) {
320                Log.w("colsMap", e.getMessage());
321            }
322
323            try {
324                mColumnMmsReadReport = cursor.getColumnIndexOrThrow(Mms.READ_REPORT);
325            } catch (IllegalArgumentException e) {
326                Log.w("colsMap", e.getMessage());
327            }
328
329            try {
330                mColumnMmsErrorType = cursor.getColumnIndexOrThrow(PendingMessages.ERROR_TYPE);
331            } catch (IllegalArgumentException e) {
332                Log.w("colsMap", e.getMessage());
333            }
334
335            try {
336                mColumnMmsLocked = cursor.getColumnIndexOrThrow(Mms.LOCKED);
337            } catch (IllegalArgumentException e) {
338                Log.w("colsMap", e.getMessage());
339            }
340        }
341    }
342}
343