/*
* Kontalk Android client
* Copyright (C) 2017 Kontalk Devteam <devteam@kontalk.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kontalk.ui.view;
import android.content.Context;
import android.graphics.Typeface;
import android.support.v4.content.ContextCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.util.AttributeSet;
import android.widget.Checkable;
import android.widget.ImageView;
import android.widget.TextView;
import org.kontalk.BuildConfig;
import org.kontalk.R;
import org.kontalk.data.Contact;
import org.kontalk.data.Conversation;
import org.kontalk.message.CompositeMessage;
import org.kontalk.message.GroupCommandComponent;
import org.kontalk.provider.MessagesProviderUtils.GroupThreadContent;
import org.kontalk.provider.MyMessages.Messages;
import org.kontalk.provider.MyMessages.Threads;
import org.kontalk.util.MessageUtils;
public class ConversationListItem extends AvatarListItem implements Checkable {
private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };
private static final StyleSpan STYLE_BOLD = new StyleSpan(Typeface.BOLD);
private static final StyleSpan STYLE_ITALIC = new StyleSpan(Typeface.ITALIC);
private Conversation mConversation;
private TextView mSubjectView;
private TextView mFromView;
private TextView mDateView;
private ImageView mSticky;
private ImageView mErrorIndicator;
private TextView mCounterView;
private boolean mChecked;
public ConversationListItem(Context context) {
super(context);
}
public ConversationListItem(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mFromView = (TextView) findViewById(R.id.from);
mSubjectView = (TextView) findViewById(R.id.subject);
mDateView = (TextView) findViewById(R.id.date);
mSticky = (ImageView) findViewById(R.id.sticky);
mErrorIndicator = (ImageView) findViewById(R.id.error);
mCounterView = (TextView) findViewById(R.id.counter);
if (isInEditMode()) {
mFromView.setText("Test zio");
mSubjectView.setText("Bella zio senti per domani facciamo che mi vieni a prendere ok?");
mDateView.setText("20:14");
/*
mErrorIndicator.setVisibility(VISIBLE);
mErrorIndicator.setImageResource(R.drawable.ic_msg_pending);
*/
mCounterView.setVisibility(VISIBLE);
mCounterView.setText("5");
}
}
public final void bind(Context context, final Conversation conv) {
mConversation = conv;
setChecked(false);
Contact contact;
// used for the conversation subject: either group subject or contact name
String recipient = null;
if (mConversation.isGroupChat()) {
recipient = mConversation.getGroupSubject();
if (TextUtils.isEmpty(recipient))
recipient = context.getString(R.string.group_untitled);
loadAvatar(null);
}
else {
contact = mConversation.getContact();
if (contact != null) {
recipient = contact.getDisplayName();
}
if (recipient == null) {
if (BuildConfig.DEBUG) {
recipient = conv.getRecipient();
}
else {
recipient = context.getString(R.string.peer_unknown);
}
}
loadAvatar(contact);
}
SpannableStringBuilder from = new SpannableStringBuilder(recipient);
if (conv.getUnreadCount() > 0)
from.setSpan(STYLE_BOLD, 0, from.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
// draft indicator
int lastpos = from.length();
String draft = conv.getDraft();
if (draft != null) {
from.append(" ");
from.append(context.getResources().getString(R.string.has_draft));
from.setSpan(new ForegroundColorSpan(
ContextCompat.getColor(context, R.color.text_color_draft)),
lastpos, from.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
}
mFromView.setText(from);
mDateView.setText(MessageUtils.formatTimeStampString(context, conv.getDate()));
mSticky.setVisibility(conv.isSticky() ? VISIBLE : GONE);
// error indicator
int resId = -1;
int statusId = -1;
switch (conv.getStatus()) {
case Messages.STATUS_SENDING:
// use pending icon even for errors
case Messages.STATUS_ERROR:
case Messages.STATUS_PENDING:
case Messages.STATUS_QUEUED:
resId = R.drawable.ic_msg_pending;
statusId = R.string.msg_status_sending;
break;
case Messages.STATUS_SENT:
resId = R.drawable.ic_msg_sent;
statusId = R.string.msg_status_sent;
break;
case Messages.STATUS_RECEIVED:
resId = R.drawable.ic_msg_delivered;
statusId = R.string.msg_status_delivered;
break;
// here we use the error icon
case Messages.STATUS_NOTACCEPTED:
resId = R.drawable.ic_thread_error;
statusId = R.string.msg_status_notaccepted;
break;
case Messages.STATUS_NOTDELIVERED:
resId = R.drawable.ic_msg_notdelivered;
statusId = R.string.msg_status_notdelivered;
break;
}
// no matching resource or draft - hide status icon
boolean incoming = resId < 0;
if (incoming || draft != null) {
mErrorIndicator.setVisibility(GONE);
int unread = mConversation.getUnreadCount();
if (unread > 0) {
mCounterView.setText(String.valueOf(unread));
mCounterView.setVisibility(VISIBLE);
}
else {
mCounterView.setVisibility(GONE);
}
}
else {
mCounterView.setVisibility(GONE);
mErrorIndicator.setVisibility(VISIBLE);
mErrorIndicator.setImageResource(resId);
mErrorIndicator.setContentDescription(getResources().getString(statusId));
}
CharSequence text;
// last message or draft??
if (conv.getRequestStatus() == Threads.REQUEST_WAITING) {
text = new SpannableString(context.getString(R.string.text_invitation_info));
((Spannable) text).setSpan(STYLE_ITALIC, 0, text.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
}
else {
String subject = conv.getSubject();
String source = (draft != null) ? draft : subject;
if (source != null) {
if (GroupCommandComponent.supportsMimeType(conv.getMime()) && draft == null) {
if (incoming) {
// content is in a special format
GroupThreadContent parsed = GroupThreadContent.parseIncoming(subject);
subject = parsed.command;
}
text = new SpannableString(GroupCommandComponent.getTextContent(getContext(), subject, incoming));
((Spannable) text).setSpan(STYLE_ITALIC, 0, text.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
}
else {
if (incoming && conv.isGroupChat()) {
// content is in a special format
GroupThreadContent parsed = GroupThreadContent.parseIncoming(subject);
contact = parsed.sender != null ? Contact.findByUserId(context, parsed.sender) : null;
source = parsed.command;
String displayName = null;
if (contact != null)
displayName = contact.getDisplayName();
if (displayName == null) {
if (BuildConfig.DEBUG) {
displayName = conv.getRecipient();
}
else {
displayName = context.getString(R.string.peer_unknown);
}
}
if (source == null) {
// determine from mime type
source = CompositeMessage.getSampleTextContent(conv.getMime());
}
text = new SpannableString(displayName + ": " + source);
((Spannable) text).setSpan(STYLE_ITALIC, 0, displayName.length()+1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
}
else {
text = source;
}
}
}
else if (conv.isEncrypted()) {
text = context.getString(R.string.text_encrypted);
}
else {
// determine from mime type
text = CompositeMessage.getSampleTextContent(conv.getMime());
}
}
if (conv.getUnreadCount() > 0) {
text = new SpannableString(text);
((Spannable) text).setSpan(STYLE_BOLD, 0, text.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
}
mSubjectView.setText(text);
}
public final void unbind() {
// TODO unbind (contact?)
}
public Conversation getConversation() {
return mConversation;
}
@Override
protected boolean isGroupChat() {
return mConversation != null && mConversation.isGroupChat();
}
@Override
public boolean isChecked() {
return mChecked;
}
@Override
public void setChecked(boolean checked) {
if (checked != mChecked) {
mChecked = checked;
refreshDrawableState();
}
}
@Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
@Override
public void toggle() {
setChecked(!mChecked);
}
}