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

Java tutorial

Introduction

Here is the source code for net.kourlas.voipms_sms.adapters.ConversationsRecyclerViewAdapter.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.graphics.Typeface;
import android.net.Uri;
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.text.style.StyleSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import net.kourlas.voipms_sms.R;
import net.kourlas.voipms_sms.activities.ConversationsActivity;
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 ConversationsRecyclerViewAdapter extends
        RecyclerView.Adapter<ConversationsRecyclerViewAdapter.ConversationViewHolder> implements Filterable {
    private final Context applicationContext;
    private final Database database;
    private final Preferences preferences;
    private final LinearLayoutManager layoutManager;

    private final ConversationsActivity activity;

    private final List<Message> messages;
    private final List<Boolean> checkedItems;
    private String filterConstraint;
    private String oldFilterConstraint;

    public ConversationsRecyclerViewAdapter(ConversationsActivity activity, LinearLayoutManager layoutManager) {
        this.applicationContext = activity.getApplicationContext();
        this.database = Database.getInstance(applicationContext);
        this.preferences = Preferences.getInstance(applicationContext);
        this.layoutManager = layoutManager;

        this.activity = activity;

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

    @Override
    public ConversationViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.conversations_item, viewGroup,
                false);
        return new ConversationViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(ConversationViewHolder conversationViewHolder, int position) {
        Message message = messages.get(position);

        ViewSwitcher viewSwitcher = conversationViewHolder.getViewSwitcher();
        viewSwitcher.setDisplayedChild(isItemChecked(position) ? 1 : 0);

        QuickContactBadge contactBadge = conversationViewHolder.getContactBadge();
        contactBadge.assignContactFromPhone(message.getContact(), true);

        String photoUri = Utils.getContactPhotoUri(applicationContext, message.getContact());
        if (photoUri != null) {
            contactBadge.setImageURI(Uri.parse(photoUri));
        } else {
            contactBadge.setImageToDefault();
        }

        TextView contactTextView = conversationViewHolder.getContactTextView();
        String contactName = Utils.getContactName(applicationContext, message.getContact());
        SpannableStringBuilder contactTextBuilder = new SpannableStringBuilder();
        if (contactName != null) {
            contactTextBuilder.append(contactName);
        } else {
            contactTextBuilder.append(Utils.getFormattedPhoneNumber(message.getContact()));
        }
        if (!filterConstraint.equals("")) {
            int index = contactTextBuilder.toString().toLowerCase().indexOf(filterConstraint.toLowerCase());
            if (index != -1) {
                contactTextBuilder.setSpan(
                        new BackgroundColorSpan(ContextCompat.getColor(applicationContext, R.color.highlight)),
                        index, index + filterConstraint.length(), SpannableString.SPAN_INCLUSIVE_EXCLUSIVE);
            }
        }
        contactTextView.setText(contactTextBuilder);

        final TextView messageTextView = conversationViewHolder.getMessageTextView();
        SpannableStringBuilder messageTextBuilder = new SpannableStringBuilder();

        int index = message.getText().toLowerCase().indexOf(filterConstraint.toLowerCase());
        if (!filterConstraint.equals("") && index != -1) {
            int nonMessageOffset = index;
            if (message.getType() == Message.Type.OUTGOING) {
                messageTextBuilder.insert(0,
                        applicationContext.getString(R.string.conversations_message_you) + " ");
                nonMessageOffset += 5;
            }

            int substringOffset = index - 20;
            if (substringOffset > 0) {
                messageTextBuilder.append("...");
                nonMessageOffset += 3;

                while (message.getText().charAt(substringOffset) != ' ' && substringOffset < index - 1) {
                    substringOffset += 1;
                }
                substringOffset += 1;
            } else {
                substringOffset = 0;
            }

            messageTextBuilder.append(message.getText().substring(substringOffset));
            messageTextBuilder.setSpan(
                    new BackgroundColorSpan(ContextCompat.getColor(applicationContext, R.color.highlight)),
                    nonMessageOffset - substringOffset,
                    nonMessageOffset - substringOffset + filterConstraint.length(),
                    SpannableString.SPAN_INCLUSIVE_EXCLUSIVE);
        } else {
            if (message.getType() == Message.Type.OUTGOING) {
                messageTextBuilder.append(applicationContext.getString(R.string.conversations_message_you));
                messageTextBuilder.append(" ");
            }
            messageTextBuilder.append(message.getText());
        }
        messageTextView.setText(messageTextBuilder);

        if (message.isUnread()) {
            contactTextView.setTypeface(null, Typeface.BOLD);
            messageTextView.setTypeface(null, Typeface.BOLD);
        } else {
            contactTextView.setTypeface(null, Typeface.NORMAL);
            messageTextView.setTypeface(null, Typeface.NORMAL);
        }

        // Set date line
        TextView dateTextView = conversationViewHolder.getDateTextView();
        if (message.isDraft()) {
            SpannableStringBuilder dateTextBuilder = new SpannableStringBuilder();
            dateTextBuilder.append(applicationContext.getString(R.string.conversations_message_draft));
            dateTextBuilder.setSpan(new StyleSpan(Typeface.ITALIC), 0, dateTextBuilder.length(),
                    Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
            dateTextView.setText(dateTextBuilder);
        } else if (!message.isDelivered()) {
            if (!message.isDeliveryInProgress()) {
                SpannableStringBuilder dateTextBuilder = new SpannableStringBuilder();
                dateTextBuilder.append(applicationContext.getString(R.string.conversations_message_not_sent));
                dateTextBuilder.setSpan(
                        new ForegroundColorSpan(
                                ContextCompat.getColor(applicationContext, android.R.color.holo_red_dark)),
                        0, dateTextBuilder.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
                dateTextView.setText(dateTextBuilder);
            } else {
                dateTextView.setText(applicationContext.getString(R.string.conversations_message_sending));
            }
        } else {
            dateTextView.setText(Utils.getFormattedDate(applicationContext, message.getDate(), true));
        }
    }

    @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 ConversationsFilter();
    }

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

    public class ConversationViewHolder extends RecyclerView.ViewHolder {
        private ViewSwitcher viewSwitcher;
        private QuickContactBadge contactBadge;
        private TextView contactTextView;
        private TextView messageTextView;
        private TextView dateTextView;

        ConversationViewHolder(View itemView) {
            super(itemView);

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

            viewSwitcher = (ViewSwitcher) itemView.findViewById(R.id.view_switcher);
            contactBadge = (QuickContactBadge) itemView.findViewById(R.id.photo);
            Utils.applyCircularMask(contactBadge);
            ImageView contactBadgeChecked = (ImageView) itemView.findViewById(R.id.conversations_photo_checked);
            Utils.applyCircularMask(contactBadgeChecked);
            contactTextView = (TextView) itemView.findViewById(R.id.contact);
            messageTextView = (TextView) itemView.findViewById(R.id.message);
            dateTextView = (TextView) itemView.findViewById(R.id.date);
        }

        ViewSwitcher getViewSwitcher() {
            return viewSwitcher;
        }

        QuickContactBadge getContactBadge() {
            return contactBadge;
        }

        TextView getContactTextView() {
            return contactTextView;
        }

        TextView getMessageTextView() {
            return messageTextView;
        }

        TextView getDateTextView() {
            return dateTextView;
        }
    }

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

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

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

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

            return results;
        }

        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            int position = layoutManager.findFirstVisibleItemPosition();

            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.equalsConversation(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).equalsConversation(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);
            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.conversations_no_messages));
                } else {
                    emptyTextView.setText(
                            applicationContext.getString(R.string.conversations_no_results, filterConstraint));
                }
            } else {
                emptyTextView.setText("");
            }

            layoutManager.scrollToPosition(position);
        }
    }
}