com.aboveware.sms.ui.MessageListAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.aboveware.sms.ui.MessageListAdapter.java

Source

/*
 * Copyright (C) 2008, 2013 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 com.aboveware.sms.ui;

import java.util.regex.Pattern;

import android.annotation.TargetApi;
import android.content.Context;
import android.database.Cursor;
import android.os.Build;
import android.os.Handler;
import android.provider.Telephony.TextBasedSmsColumns;
import android.support.v4.widget.CursorAdapter;
import android.util.Log;
import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ListView;

import com.aboveware.sms.R;
import com.aboveware.sms.conversations.Conversation;
import com.aboveware.sms.conversations.ConversationsDatabase;
import com.aboveware.sms.conversations.Message;
import com.aboveware.sms.conversations.PadConversations;

/**
 * The back-end data adapter of a message list.
 */
public class MessageListAdapter extends CursorAdapter {
    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
    private static class MessageItemCache extends LruCache<Long, MessageItem> {
        public MessageItemCache(int maxSize) {
            super(maxSize);
        }
    }

    public interface OnDataSetChangedListener {
        void onContentChanged(MessageListAdapter adapter);

        void onDataSetChanged(MessageListAdapter adapter);
    }

    private static final String TAG = MessageListAdapter.class.getSimpleName();

    private static final int CACHE_SIZE = 50;

    public static int INCOMING_ITEM_TYPE_SMS = 0;
    public static int OUTGOING_ITEM_TYPE_SMS = 1;

    protected LayoutInflater mInflater;
    private final MessageItemCache mMessageItemCache;
    private OnDataSetChangedListener mOnDataSetChangedListener;
    private Handler mMsgListItemHandler;

    private Pattern mHighlight;

    private Context mContext;

    private boolean mIsGroupConversation;

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public MessageListAdapter(Context context, ListView listView, Pattern highlight) {
        super(context, null, 0);
        mContext = context;
        mHighlight = highlight;

        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mMessageItemCache = new MessageItemCache(CACHE_SIZE);

        listView.setRecyclerListener(new AbsListView.RecyclerListener() {
            @Override
            public void onMovedToScrapHeap(View view) {
                if (view instanceof MessageListItem) {
                    MessageListItem mli = (MessageListItem) view;
                    // Clear references to resources
                    mli.unbind();
                }
            }
        });
    }

    @Override
    public boolean areAllItemsEnabled() {
        return true;
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        if (view instanceof MessageListItem) {
            String type = cursor.getString(ConversationsDatabase.messageTypeIndex);
            long msgId = cursor.getLong(ConversationsDatabase.messageIdIndex);

            MessageItem msgItem = getCachedMessageItem(type, msgId, cursor);
            if (msgItem != null) {
                MessageListItem mli = (MessageListItem) view;
                int position = cursor.getPosition();
                mli.bind(msgItem, mIsGroupConversation, position);
                mli.setMsgListItemHandler(mMsgListItemHandler);
            }
        }
    }

    public void cancelBackgroundLoading() {
        mMessageItemCache.evictAll(); // causes entryRemoved to be called for each MessageItem in the cache which causes us to cancel
                                      // loading of background pdu's and images.
    }

    public MessageItem getCachedMessageItem(String type, long msgId, Cursor cursor) {
        MessageItem item = mMessageItemCache.get(msgId);
        if (item == null && cursor != null && isCursorValid(cursor)) {
            try {
                long threadId = cursor.getLong(ConversationsDatabase.messageThreadIdIndex);
                Conversation conversation = PadConversations.getConversation(threadId);
                // Skip the draft message, it has SMS id == 0
                if (msgId != 0) {
                    Message message = conversation.getMessage(msgId);
                    if (message == null)
                        Log.w(MessageListActivity.class.getName(), "MsgID " + msgId + " is null");
                    else
                        Log.w(MessageListActivity.class.getName(), "MsgID " + msgId + message.getAddress());
                    item = new MessageItem(mContext, conversation.getPadRecipients().get(0).getContact(), type,
                            message, mHighlight);
                    mMessageItemCache.put(item.msgId, item);
                }
            } catch (Exception e) {
                Log.e(TAG, "getCachedMessageItem: ", e);
            }
        }
        return item;
    }

    public Cursor getCursorForItem(MessageItem item) {
        Cursor cursor = getCursor();
        if (isCursorValid(cursor)) {
            if (cursor.moveToFirst()) {
                do {
                    long id = cursor.getLong(mRowIDColumn);
                    if (id == item.msgId) {
                        return cursor;
                    }
                } while (cursor.moveToNext());
            }
        }
        return null;
    }

    private int getItemViewType(Cursor cursor) {
        int boxId = cursor.getInt(ConversationsDatabase.messageTypeIndex);
        // Note that messages from the SIM card all have a boxId of zero.
        return (boxId == TextBasedSmsColumns.MESSAGE_TYPE_INBOX || boxId == TextBasedSmsColumns.MESSAGE_TYPE_ALL)
                ? INCOMING_ITEM_TYPE_SMS
                : OUTGOING_ITEM_TYPE_SMS;
    }

    @Override
    public int getItemViewType(int position) {
        Cursor cursor = (Cursor) getItem(position);
        return getItemViewType(cursor);
    }

    public int getPosition(long msgId) {
        int position = 0;
        Cursor cursor = getCursor();
        if (isCursorValid(cursor)) {
            if (cursor.moveToFirst()) {
                do {
                    long id = cursor.getLong(mRowIDColumn);
                    if (id == msgId) {
                        return position;
                    }
                    ++position;
                } while (cursor.moveToNext());
            }
        }
        return -1;
    }

    /*
     * MessageListAdapter says that it contains two types of views. Really, it just contains a single type, a MessageListItem.
     * Depending upon whether the message is an incoming or outgoing message, the avatar and text and other items are laid out
     * either left or right justified. That works fine for everything but the message text. When views are recycled, there's a
     * greater than zero chance that the right-justified text on outgoing messages will remain left-justified. The best solution at
     * this point is to tell the adapter we've got two different types of views. That way we won't recycle views between the two
     * types.
     * 
     * @see android.widget.BaseAdapter#getViewTypeCount()
     */
    @Override
    public int getViewTypeCount() {
        return 2; // Incoming and outgoing messages
    }

    private boolean isCursorValid(Cursor cursor) {
        // Check whether the cursor is valid or not.
        if (cursor == null || cursor.isClosed() || cursor.isBeforeFirst() || cursor.isAfterLast()) {
            return false;
        }
        return true;
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        return mInflater.inflate(getItemViewType(cursor) == INCOMING_ITEM_TYPE_SMS ? R.layout.message_list_item_recv
                : R.layout.message_list_item_send, parent, false);
    }

    @Override
    public void notifyDataSetChanged() {
        super.notifyDataSetChanged();
        mMessageItemCache.evictAll();

        if (mOnDataSetChangedListener != null) {
            mOnDataSetChangedListener.onDataSetChanged(this);
        }
    }

    @Override
    protected void onContentChanged() {
        if (getCursor() != null && !getCursor().isClosed()) {
            if (mOnDataSetChangedListener != null) {
                mOnDataSetChangedListener.onContentChanged(this);
            }
        }
    }

    public void setIsGroupConversation(boolean isGroup) {
        mIsGroupConversation = isGroup;
    }

    public void setMsgListItemHandler(Handler handler) {
        mMsgListItemHandler = handler;
    }

    public void setOnDataSetChangedListener(OnDataSetChangedListener listener) {
        mOnDataSetChangedListener = listener;
    }
}