/*
* 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);
}
}
}
}
}