/* * Copyright (C) 2008 Esmertec AG. * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.edu.tsinghua.hpc.tmms.ui; import java.util.LinkedHashMap; import java.util.Map; import android.content.Context; import android.database.Cursor; import android.os.Handler; import android.os.Message; import android.provider.BaseColumns; import android.provider.Telephony.Mms; import android.provider.Telephony.MmsSms; import android.provider.Telephony.MmsSms.PendingMessages; import android.provider.Telephony.Sms; import android.provider.Telephony.Sms.Conversations; import android.util.Config; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CursorAdapter; import android.widget.ListView; import cn.edu.tsinghua.hpc.google.tmms.MmsException; import cn.edu.tsinghua.hpc.tmms.R; import cn.edu.tsinghua.hpc.tmms.syncaction.TMmsSmsCursor; /** * The back-end data adapter of a message list. */ public class MessageListAdapter extends CursorAdapter { private static final String TAG = "MessageListAdapter"; private static final boolean DEBUG = false; private static final boolean LOCAL_LOGV = Config.LOGV && DEBUG; static final String[] PROJECTION = new String[] { // TODO: should move this symbol into android.provider.Telephony. MmsSms.TYPE_DISCRIMINATOR_COLUMN, BaseColumns._ID, Conversations.THREAD_ID, "guid", "sync_state", // For SMS Sms.ADDRESS, Sms.BODY, Sms.DATE, Sms.READ, Sms.TYPE, Sms.STATUS, Sms.LOCKED, // For MMS Mms.SUBJECT, Mms.SUBJECT_CHARSET, Mms.DATE, Mms.READ, Mms.MESSAGE_TYPE, Mms.MESSAGE_BOX, Mms.DELIVERY_REPORT, Mms.READ_REPORT, PendingMessages.ERROR_TYPE, Mms.LOCKED }; // The indexes of the default columns which must be consistent // with above PROJECTION. static final int COLUMN_MSG_TYPE = 0; static final int COLUMN_ID = 1; static final int COLUMN_THREAD_ID = 2; static final int COLUMN_GUID = 3; static final int COLUMN_SYNC_STATE = 4; static final int COLUMN_SMS_ADDRESS = 5; static final int COLUMN_SMS_BODY = 6; static final int COLUMN_SMS_DATE = 7; static final int COLUMN_SMS_READ = 8; static final int COLUMN_SMS_TYPE = 9; static final int COLUMN_SMS_STATUS = 10; static final int COLUMN_SMS_LOCKED = 11; static final int COLUMN_MMS_SUBJECT = 12; static final int COLUMN_MMS_SUBJECT_CHARSET = 13; static final int COLUMN_MMS_DATE = 14; static final int COLUMN_MMS_READ = 15; static final int COLUMN_MMS_MESSAGE_TYPE = 16; static final int COLUMN_MMS_MESSAGE_BOX = 17; static final int COLUMN_MMS_DELIVERY_REPORT = 18; static final int COLUMN_MMS_READ_REPORT = 19; static final int COLUMN_MMS_ERROR_TYPE = 20; static final int COLUMN_MMS_LOCKED = 21; private static final int CACHE_SIZE = 50; protected LayoutInflater mInflater; private final ListView mListView; private final LinkedHashMap<Long, MessageItem> mMessageItemCache; private final ColumnsMap mColumnsMap; private OnDataSetChangedListener mOnDataSetChangedListener; private Handler mMsgListItemHandler; private String mHighlight; private Context mContext; private boolean mAutoRequery; static final int READY = 0; static final int LOADING = 1; static final int LOADED = 2; private int mLoading = READY; public String recipient; public final TMmsSmsCursor mTCursor; public MessageListAdapter(Context context, Cursor c, ListView listView, boolean useDefaultColumnsMap, String highlight, boolean autoRequery) { super(context, c, autoRequery /* auto-requery */); mHighlight = highlight != null ? highlight.toLowerCase() : null; mInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); mListView = listView; mMessageItemCache = new LinkedHashMap<Long, MessageItem>(10, 1.0f, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > CACHE_SIZE; } }; if (useDefaultColumnsMap) { mColumnsMap = new ColumnsMap(); } else { mColumnsMap = new ColumnsMap(c); } mTCursor = new TMmsSmsCursor(context, new String[] {}); mContext = context; mAutoRequery = autoRequery; } public void setAddress(String address) { Log.d("Mms", "set address " + address); recipient = address; if ((this.getCursor() == null || this.getCursor().getCount() == 0) && recipient != null && !recipient.equals("")) { preloadRemoteSMS(); } if (recipient == null) { mLoading = LOADED; } } public class UpdateListHandler extends Handler { @Override public void handleMessage(Message msg) { Log.d("Mms", "update list handler"); onRefeshContent(); ((ComposeMessageActivity) mContext).getListView().invalidate(); } } private void onRefeshContent() { this.notifyDataSetChanged(); } public final UpdateListHandler handler = new UpdateListHandler(); public void preloadRemoteSMS() { mTCursor.clearAllSms(mContext.getContentResolver()); // currentPage++; // new Timer().schedule(new RetriveAchivedSMSTimerTask(5, recipient, // currentPage), 0); } @Override public void bindView(View view, Context context, Cursor cursor) { if (view instanceof MessageListItem) { String type = cursor.getString(mColumnsMap.mColumnMsgType); long msgId = cursor.getLong(mColumnsMap.mColumnMsgId); MessageItem msgItem = getCachedMessageItem(type, msgId, cursor); if (msgItem != null) { ((MessageListItem) view).bind(msgItem); ((MessageListItem) view) .setMsgListItemHandler(mMsgListItemHandler); } } } public void retriveMoreAction() { mTCursor.retrieveArchivedSMS(recipient, handler); } /* private int getRealPosition(int position) { switch (mLoading) { case READY: case LOADING: return position - 1; case LOADED: default: return position; } } @Override public int getCount() { switch (this.mLoading) { case READY: case LOADING: return super.getCount() + 1; case LOADED: return super.getCount(); } return super.getCount(); } public View getView(int position, View convertView, ViewGroup parent) { try { if (!mDataValid) { throw new IllegalStateException( "this should only be called when the cursor is valid"); } if (!mCursor.moveToPosition(getRealPosition(position))) { throw new IllegalStateException( "couldn't move cursor to position " + position); } View v; if (convertView == null) { v = newView(mContext, mCursor, parent); } else { if (convertView instanceof Button) { v = newView(mContext, mCursor, parent); } else { v = convertView; } } bindView(v, mContext, mCursor); return v; } catch (Exception e) { if (mLoading == READY || mLoading == LOADING) { if (position == getCount()-1) { Button b = new Button(mContext); b.setText("More..."); b.setOnClickListener(new OnClickListener() { public void onClick(View v) { mLoading = LOADING; handler.sendEmptyMessage(0); mTCursor.retrieveArchivedSMS(recipient, handler); } }); return b; } else { return super.getView(position, convertView, parent); } } else { return super.getView(position, convertView, parent); } } }*/ public interface OnDataSetChangedListener { void onDataSetChanged(MessageListAdapter adapter); void onContentChanged(MessageListAdapter adapter); } public void setOnDataSetChangedListener(OnDataSetChangedListener l) { mOnDataSetChangedListener = l; } public void setMsgListItemHandler(Handler handler) { mMsgListItemHandler = handler; } @Override public void notifyDataSetChanged() { super.notifyDataSetChanged(); if (LOCAL_LOGV) { Log.v(TAG, "MessageListAdapter.notifyDataSetChanged()."); } mListView.setSelection(mListView.getCount()); mMessageItemCache.clear(); if (mOnDataSetChangedListener != null) { mOnDataSetChangedListener.onDataSetChanged(this); } } @Override protected void onContentChanged() { if (mAutoRequery) { super.onContentChanged(); return; } if (this.getCursor() != null && !this.getCursor().isClosed()) { if (mOnDataSetChangedListener != null) { mOnDataSetChangedListener.onContentChanged(this); } } } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return mInflater.inflate(R.layout.message_list_item, parent, false); } public MessageItem getCachedMessageItem(String type, long msgId, Cursor c) { MessageItem item = mMessageItemCache.get(getKey(type, msgId)); if (item == null) { try { item = new MessageItem(mContext, type, c, mColumnsMap, mHighlight); mMessageItemCache.put(getKey(item.mType, item.mMsgId), item); } catch (MmsException e) { Log.e(TAG, e.getMessage()); } } return item; } private static long getKey(String type, long id) { if (type.equals("mms")) { return -id; } else { return id; } } public static class ColumnsMap { public int mColumnMsgType; public int mColumnMsgId; public int mColumnMsgGuid; public int mColumnMsgSyncState; public int mColumnSmsAddress; public int mColumnSmsBody; public int mColumnSmsDate; public int mColumnSmsRead; public int mColumnSmsType; public int mColumnSmsStatus; public int mColumnSmsLocked; public int mColumnMmsSubject; public int mColumnMmsSubjectCharset; public int mColumnMmsDate; public int mColumnMmsRead; public int mColumnMmsMessageType; public int mColumnMmsMessageBox; public int mColumnMmsDeliveryReport; public int mColumnMmsReadReport; public int mColumnMmsErrorType; public int mColumnMmsLocked; public ColumnsMap() { mColumnMsgType = COLUMN_MSG_TYPE; mColumnMsgId = COLUMN_ID; mColumnMsgGuid = COLUMN_GUID; mColumnMsgSyncState = COLUMN_SYNC_STATE; mColumnSmsAddress = COLUMN_SMS_ADDRESS; mColumnSmsBody = COLUMN_SMS_BODY; mColumnSmsDate = COLUMN_SMS_DATE; mColumnSmsType = COLUMN_SMS_TYPE; mColumnSmsStatus = COLUMN_SMS_STATUS; mColumnSmsLocked = COLUMN_SMS_LOCKED; mColumnMmsSubject = COLUMN_MMS_SUBJECT; mColumnMmsSubjectCharset = COLUMN_MMS_SUBJECT_CHARSET; mColumnMmsMessageType = COLUMN_MMS_MESSAGE_TYPE; mColumnMmsMessageBox = COLUMN_MMS_MESSAGE_BOX; mColumnMmsDeliveryReport = COLUMN_MMS_DELIVERY_REPORT; mColumnMmsReadReport = COLUMN_MMS_READ_REPORT; mColumnMmsErrorType = COLUMN_MMS_ERROR_TYPE; mColumnMmsLocked = COLUMN_MMS_LOCKED; } public ColumnsMap(Cursor cursor) { // Ignore all 'not found' exceptions since the custom columns // may be just a subset of the default columns. try { mColumnMsgType = cursor .getColumnIndexOrThrow(MmsSms.TYPE_DISCRIMINATOR_COLUMN); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnMsgId = cursor.getColumnIndexOrThrow(BaseColumns._ID); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnSmsAddress = cursor.getColumnIndexOrThrow(Sms.ADDRESS); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnSmsBody = cursor.getColumnIndexOrThrow(Sms.BODY); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnSmsDate = cursor.getColumnIndexOrThrow(Sms.DATE); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnSmsType = cursor.getColumnIndexOrThrow(Sms.TYPE); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnSmsStatus = cursor.getColumnIndexOrThrow(Sms.STATUS); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnSmsLocked = cursor.getColumnIndexOrThrow(Sms.LOCKED); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnMmsSubject = cursor.getColumnIndexOrThrow(Mms.SUBJECT); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnMmsSubjectCharset = cursor .getColumnIndexOrThrow(Mms.SUBJECT_CHARSET); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnMmsMessageType = cursor .getColumnIndexOrThrow(Mms.MESSAGE_TYPE); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnMmsMessageBox = cursor .getColumnIndexOrThrow(Mms.MESSAGE_BOX); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnMmsDeliveryReport = cursor .getColumnIndexOrThrow(Mms.DELIVERY_REPORT); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnMmsReadReport = cursor .getColumnIndexOrThrow(Mms.READ_REPORT); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnMmsErrorType = cursor .getColumnIndexOrThrow(PendingMessages.ERROR_TYPE); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } try { mColumnMmsLocked = cursor.getColumnIndexOrThrow(Mms.LOCKED); } catch (IllegalArgumentException e) { Log.w("colsMap", e.getMessage()); } } } }