net.kourlas.voipms_sms.adapters.ConversationRecyclerViewAdapter.java Source code

Java tutorial

Introduction

Here is the source code for net.kourlas.voipms_sms.adapters.ConversationRecyclerViewAdapter.java

Source

/*
 * VoIP.ms SMS
 * Copyright (C) 2015-2016 Michael Kourlas
 *
 * 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 net.kourlas.voipms_sms.adapters;

import android.content.Context;
import android.net.Uri;
import android.provider.ContactsContract;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.QuickContactBadge;
import android.widget.TextView;
import net.kourlas.voipms_sms.R;
import net.kourlas.voipms_sms.activities.ConversationActivity;
import net.kourlas.voipms_sms.db.Database;
import net.kourlas.voipms_sms.model.Message;
import net.kourlas.voipms_sms.preferences.Preferences;
import net.kourlas.voipms_sms.utils.Utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ConversationRecyclerViewAdapter
        extends RecyclerView.Adapter<ConversationRecyclerViewAdapter.MessageViewHolder> implements Filterable {
    private static final int ITEM_LEFT_PRIMARY = 0;
    private static final int ITEM_LEFT_SECONDARY = 1;
    private static final int ITEM_RIGHT_PRIMARY = 2;
    private static final int ITEM_RIGHT_SECONDARY = 3;

    private final ConversationActivity activity;
    private final Context applicationContext;
    private final LinearLayoutManager layoutManager;
    private final String contact;
    private final List<Message> messages;
    private final List<Boolean> checkedItems;
    private final Preferences preferences;
    private final Database database;

    private String filterConstraint;
    private String oldFilterConstraint;

    public ConversationRecyclerViewAdapter(ConversationActivity activity, LinearLayoutManager layoutManager,
            String contact) {
        this.activity = activity;
        this.applicationContext = activity.getApplicationContext();
        this.layoutManager = layoutManager;
        this.preferences = Preferences.getInstance(applicationContext);
        this.database = Database.getInstance(applicationContext);

        this.contact = contact;
        this.messages = new ArrayList<>();
        this.filterConstraint = "";
        this.oldFilterConstraint = "";
        this.checkedItems = new ArrayList<>();
    }

    @Override
    public MessageViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        View itemView = null;
        switch (viewType) {
        case ITEM_LEFT_PRIMARY:
            itemView = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.conversation_item_incoming_primary, viewGroup, false);
            break;
        case ITEM_LEFT_SECONDARY:
            itemView = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.conversation_item_incoming_secondary, viewGroup, false);
            break;
        case ITEM_RIGHT_PRIMARY:
            itemView = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.conversation_item_outgoing_primary, viewGroup, false);
            break;
        case ITEM_RIGHT_SECONDARY:
            itemView = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.conversation_item_outgoing_secondary, viewGroup, false);
            break;
        }
        return new MessageViewHolder(itemView, viewType);
    }

    @Override
    public void onBindViewHolder(MessageViewHolder messageViewHolder, int i) {
        Message message = messages.get(i);
        int viewType = getItemViewType(i);

        if (viewType == ITEM_LEFT_PRIMARY || viewType == ITEM_RIGHT_PRIMARY) {
            QuickContactBadge contactBadge = messageViewHolder.getContactBadge();
            if (viewType == ITEM_LEFT_PRIMARY) {
                contactBadge.assignContactFromPhone(message.getContact(), true);
            } else {
                contactBadge.assignContactFromPhone(message.getDid(), true);
            }
            String photoUri;
            if (viewType == ITEM_LEFT_PRIMARY) {
                photoUri = Utils.getContactPhotoUri(applicationContext, message.getContact());

            } else {
                photoUri = Utils.getContactPhotoUri(applicationContext, ContactsContract.Profile.CONTENT_URI);
                if (photoUri == null) {
                    photoUri = Utils.getContactPhotoUri(applicationContext, message.getDid());
                }
            }
            if (photoUri != null) {
                contactBadge.setImageURI(Uri.parse(photoUri));
            } else {
                contactBadge.setImageToDefault();
            }
        }

        View smsContainer = messageViewHolder.getSmsContainer();

        TextView messageText = messageViewHolder.getMessageText();
        SpannableStringBuilder messageTextBuilder = new SpannableStringBuilder();
        messageTextBuilder.append(message.getText());
        if (!filterConstraint.equals("")) {
            int index = message.getText().toLowerCase().indexOf(filterConstraint.toLowerCase());
            if (index != -1) {
                messageTextBuilder.setSpan(
                        new BackgroundColorSpan(ContextCompat.getColor(applicationContext, R.color.highlight)),
                        index, index + filterConstraint.length(), SpannableString.SPAN_INCLUSIVE_EXCLUSIVE);
                messageTextBuilder.setSpan(
                        new ForegroundColorSpan(ContextCompat.getColor(applicationContext, R.color.dark_gray)),
                        index, index + filterConstraint.length(), SpannableString.SPAN_INCLUSIVE_EXCLUSIVE);
            }
        }
        messageText.setText(messageTextBuilder);

        TextView dateText = messageViewHolder.getDateText();
        if (!message.isDelivered()) {
            if (!message.isDeliveryInProgress()) {
                SpannableStringBuilder dateTextBuilder = new SpannableStringBuilder();
                if (isItemChecked(i)) {
                    dateTextBuilder
                            .append(applicationContext.getString(R.string.conversation_message_not_sent_selected));
                } else {
                    dateTextBuilder.append(applicationContext.getString(R.string.conversation_message_not_sent));
                }
                dateTextBuilder.setSpan(
                        new ForegroundColorSpan(
                                isItemChecked(i) ? ContextCompat.getColor(applicationContext, android.R.color.white)
                                        : ContextCompat.getColor(applicationContext,
                                                android.R.color.holo_red_dark)),
                        0, dateTextBuilder.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
                dateText.setText(dateTextBuilder);
                dateText.setVisibility(View.VISIBLE);
            } else {
                dateText.setText(applicationContext.getString(R.string.conversation_message_sending));
                dateText.setVisibility(View.VISIBLE);
            }
        } else if (i == messages.size() - 1
                || ((viewType == ITEM_LEFT_PRIMARY || viewType == ITEM_LEFT_SECONDARY)
                        && getItemViewType(i + 1) != ITEM_LEFT_SECONDARY)
                || ((viewType == ITEM_RIGHT_PRIMARY || viewType == ITEM_RIGHT_SECONDARY)
                        && getItemViewType(i + 1) != ITEM_RIGHT_SECONDARY)) {
            dateText.setText(Utils.getFormattedDate(applicationContext, message.getDate(), false));
            dateText.setVisibility(View.VISIBLE);
        } else {
            dateText.setVisibility(View.GONE);
        }

        if (viewType == ITEM_LEFT_PRIMARY || viewType == ITEM_LEFT_SECONDARY) {
            smsContainer.setBackgroundResource(isItemChecked(i) ? android.R.color.holo_blue_dark : R.color.primary);
        } else {
            smsContainer.setBackgroundResource(
                    isItemChecked(i) ? android.R.color.holo_blue_dark : android.R.color.white);
            messageText.setTextColor(
                    isItemChecked(i) ? ContextCompat.getColor(applicationContext, android.R.color.white)
                            : ContextCompat.getColor(applicationContext, R.color.dark_gray));
            messageText.setLinkTextColor(
                    isItemChecked(i) ? ContextCompat.getColor(applicationContext, android.R.color.white)
                            : ContextCompat.getColor(applicationContext, R.color.dark_gray));
            dateText.setTextColor(
                    isItemChecked(i) ? ContextCompat.getColor(applicationContext, R.color.message_translucent_white)
                            : ContextCompat.getColor(applicationContext, R.color.message_translucent_dark_grey));
        }
    }

    public int getItemViewType(int position) {
        Message message = messages.get(position);
        Message previousMessage = null;
        if (position > 0) {
            previousMessage = messages.get(position - 1);
        }

        if (message.getType() == Message.Type.INCOMING) {
            if (previousMessage == null || previousMessage.getType() == Message.Type.OUTGOING
                    || message.getDateInDatabaseFormat() - previousMessage.getDateInDatabaseFormat() > 60) {
                return ITEM_LEFT_PRIMARY;
            } else {
                return ITEM_LEFT_SECONDARY;
            }
        } else {
            if (previousMessage == null || previousMessage.getType() == Message.Type.INCOMING
                    || message.getDateInDatabaseFormat() - previousMessage.getDateInDatabaseFormat() > 60) {
                return ITEM_RIGHT_PRIMARY;
            } else {
                return ITEM_RIGHT_SECONDARY;
            }
        }
    }

    @Override
    public int getItemCount() {
        return messages.size();
    }

    public boolean isItemChecked(int position) {
        return checkedItems.get(position);
    }

    public Message getItem(int position) {
        return messages.get(position);
    }

    public void toggleItemChecked(int position) {
        setItemChecked(position, !isItemChecked(position));
    }

    public void setItemChecked(int position, boolean checked) {
        boolean previous = checkedItems.get(position);
        checkedItems.set(position, checked);

        if (previous && !checked) {
            notifyItemChanged(position);
        } else if (!previous && checked) {
            notifyItemChanged(position);
        }
    }

    public int getCheckedItemCount() {
        int checkedItemCount = 0;
        for (Boolean checkedItem : checkedItems) {
            if (checkedItem) {
                checkedItemCount += 1;
            }
        }
        return checkedItemCount;
    }

    public void refresh() {
        getFilter().filter(filterConstraint);
    }

    @Override
    public Filter getFilter() {
        return new MessageFilter();
    }

    public void refresh(String newFilterConstraint) {
        getFilter().filter(newFilterConstraint);
    }

    public class MessageViewHolder extends RecyclerView.ViewHolder {
        private final QuickContactBadge contactBadge;
        private final View smsContainer;
        private final TextView messageText;
        private final TextView dateText;

        MessageViewHolder(View itemView, int viewType) {
            super(itemView);

            itemView.setClickable(true);
            itemView.setOnClickListener(activity);
            itemView.setLongClickable(true);
            itemView.setOnLongClickListener(activity);

            if (viewType == ITEM_LEFT_PRIMARY || viewType == ITEM_RIGHT_PRIMARY) {
                contactBadge = (QuickContactBadge) itemView.findViewById(R.id.photo);
                Utils.applyCircularMask(contactBadge);
            } else {
                contactBadge = null;
            }
            smsContainer = itemView.findViewById(R.id.sms_container);
            Utils.applyRoundedCornersMask(smsContainer);
            messageText = (TextView) itemView.findViewById(R.id.message);
            dateText = (TextView) itemView.findViewById(R.id.date);
        }

        QuickContactBadge getContactBadge() {
            return contactBadge;
        }

        View getSmsContainer() {
            return smsContainer;
        }

        TextView getMessageText() {
            return messageText;
        }

        TextView getDateText() {
            return dateText;
        }
    }

    class MessageFilter extends Filter {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults results = new FilterResults();

            oldFilterConstraint = filterConstraint;
            filterConstraint = constraint.toString().trim();

            List<Message> messages = database.getFilteredMessagesForConversation(preferences.getDid(), contact,
                    filterConstraint.toLowerCase());

            results.count = messages.size();
            results.values = messages;

            return results;
        }

        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            List<Message> newMessages = Collections.emptyList();
            if (results.values != null) {
                newMessages = (List<Message>) results.values;
            }

            // Remove old messages from the adapter
            List<Message> oldMessages = new ArrayList<>();
            oldMessages.addAll(messages);
            for (Message oldMessage : oldMessages) {
                boolean removed = true;
                for (Message newMessage : newMessages) {
                    if (oldMessage.equalsDatabaseId(newMessage)) {
                        removed = false;
                        break;
                    }
                }

                if (removed) {
                    // Message was removed
                    int index = messages.indexOf(oldMessage);
                    checkedItems.remove(index);
                    messages.remove(index);
                    notifyItemRemoved(index);
                }
            }

            // Update changed messages in the adapter
            List<Message> newConversationMessages = new ArrayList<>();
            newConversationMessages.addAll(newMessages);
            for (int i = 0; i < messages.size(); i++) {
                for (Message newMessage : newMessages) {
                    if (messages.get(i).equalsDatabaseId(newMessage)) {
                        // Message was changed (or the filter constraint has
                        // changed and we want a change animation for all
                        // messages)
                        if (!messages.get(i).equals(newMessage) || !oldFilterConstraint.equals(filterConstraint)) {
                            messages.set(i, newMessage);
                            notifyItemChanged(i);
                        }
                        newConversationMessages.remove(newMessage);
                    }
                }
            }
            newMessages = newConversationMessages;

            // Update moved messages in the adapter
            List<Message> sortedMessages = new ArrayList<>();
            sortedMessages.addAll(messages);
            Collections.sort(sortedMessages);
            Collections.reverse(sortedMessages);
            for (int i = 0; i < sortedMessages.size(); i++) {
                if (sortedMessages.get(i) == messages.get(i)) {
                    continue;
                }

                int index = -1;
                for (int j = 0; j < messages.size(); j++) {
                    if (messages.get(j) == sortedMessages.get(i)) {
                        index = j;
                        break;
                    }
                }

                // Message was moved
                checkedItems.add(i, checkedItems.get(index));
                checkedItems.remove(index + 1);
                messages.add(i, messages.get(index));
                messages.remove(index + 1);
                notifyItemMoved(index, i);
            }

            // Add new messages to the adapter
            for (Message newMessage : newMessages) {
                if (messages.size() >= 1) {
                    int i = 0;
                    while (i < messages.size() && newMessage.compareTo(messages.get(i)) < 0) {
                        i++;
                    }
                    checkedItems.add(i, false);
                    messages.add(i, newMessage);
                    notifyItemInserted(i);
                } else {
                    checkedItems.add(0, false);
                    messages.add(0, newMessage);
                    notifyItemInserted(0);
                }
            }

            TextView emptyTextView = (TextView) activity.findViewById(R.id.empty_text);
            if (messages.size() == 0) {
                if (filterConstraint.equals("")) {
                    emptyTextView.setText(applicationContext.getString(R.string.conversation_no_messages));
                } else {
                    emptyTextView.setText(
                            applicationContext.getString(R.string.conversation_no_results, filterConstraint));
                }
            } else {
                emptyTextView.setText("");
            }

            if (messages.size() > 1) {
                if (layoutManager.findLastVisibleItemPosition() >= messages.size() - 2) {
                    layoutManager.scrollToPosition(messages.size() - 1);
                }
            }
        }
    }
}