show message draft in conversation overview

This commit is contained in:
Daniel Gultsch 2018-03-17 19:18:22 +01:00
parent 9765f26de0
commit 11dfd87672
3 changed files with 301 additions and 248 deletions

View file

@ -3,6 +3,8 @@ package eu.siacs.conversations.entities;
import android.content.ContentValues;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import org.json.JSONArray;
import org.json.JSONException;
@ -46,13 +48,14 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public static final String ATTRIBUTE_MUTED_TILL = "muted_till";
public static final String ATTRIBUTE_ALWAYS_NOTIFY = "always_notify";
public static final String ATTRIBUTE_LAST_CLEAR_HISTORY = "last_clear_history";
public static final String ATTRIBUTE_NEXT_MESSAGE = "next_message";
private static final String ATTRIBUTE_CRYPTO_TARGETS = "crypto_targets";
private static final String ATTRIBUTE_NEXT_ENCRYPTION = "next_encryption";
static final String ATTRIBUTE_MUC_PASSWORD = "muc_password";
private static final String ATTRIBUTE_NEXT_MESSAGE = "next_message";
private static final String ATTRIBUTE_NEXT_MESSAGE_TIMESTAMP = "next_message_timestamp";
private static final String ATTRIBUTE_CRYPTO_TARGETS = "crypto_targets";
private static final String ATTRIBUTE_NEXT_ENCRYPTION = "next_encryption";
protected final ArrayList<Message> messages = new ArrayList<>();
public AtomicBoolean messagesLoaded = new AtomicBoolean(true);
protected Account account = null;
private String draftMessage;
private String name;
private String contactUuid;
@ -61,22 +64,59 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
private int status;
private long created;
private int mode;
private JSONObject attributes = new JSONObject();
private Jid nextCounterpart;
protected final ArrayList<Message> messages = new ArrayList<>();
protected Account account = null;
private transient MucOptions mucOptions = null;
private boolean messagesLeftOnServer = true;
private ChatState mOutgoingChatState = Config.DEFAULT_CHATSTATE;
private ChatState mIncomingChatState = Config.DEFAULT_CHATSTATE;
private String mFirstMamReference = null;
private Message correctingMessage;
public AtomicBoolean messagesLoaded = new AtomicBoolean(true);
public Conversation(final String name, final Account account, final Jid contactJid,
final int mode) {
this(java.util.UUID.randomUUID().toString(), name, null, account
.getUuid(), contactJid, System.currentTimeMillis(),
STATUS_AVAILABLE, mode, "");
this.account = account;
}
public Conversation(final String uuid, final String name, final String contactUuid,
final String accountUuid, final Jid contactJid, final long created, final int status,
final int mode, final String attributes) {
this.uuid = uuid;
this.name = name;
this.contactUuid = contactUuid;
this.accountUuid = accountUuid;
this.contactJid = contactJid;
this.created = created;
this.status = status;
this.mode = mode;
try {
this.attributes = new JSONObject(attributes == null ? "" : attributes);
} catch (JSONException e) {
this.attributes = new JSONObject();
}
}
public static Conversation fromCursor(Cursor cursor) {
Jid jid;
try {
jid = Jid.of(cursor.getString(cursor.getColumnIndex(CONTACTJID)));
} catch (final IllegalArgumentException e) {
// Borked DB..
jid = null;
}
return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)),
cursor.getString(cursor.getColumnIndex(NAME)),
cursor.getString(cursor.getColumnIndex(CONTACT)),
cursor.getString(cursor.getColumnIndex(ACCOUNT)),
jid,
cursor.getLong(cursor.getColumnIndex(CREATED)),
cursor.getInt(cursor.getColumnIndex(STATUS)),
cursor.getInt(cursor.getColumnIndex(MODE)),
cursor.getString(cursor.getColumnIndex(ATTRIBUTES)));
}
public boolean hasMessagesLeftOnServer() {
return messagesLeftOnServer;
@ -86,7 +126,6 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
this.messagesLeftOnServer = value;
}
public Message getFirstUnreadMessage() {
Message first = null;
synchronized (this.messages) {
@ -102,7 +141,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
}
public Message findUnsentMessageWithUuid(String uuid) {
synchronized(this.messages) {
synchronized (this.messages) {
for (final Message message : this.messages) {
final int s = message.getStatus();
if ((s == Message.STATUS_UNSEND || s == Message.STATUS_WAITING) && message.getUuid().equals(uuid)) {
@ -115,7 +154,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public void findWaitingMessages(OnMessageFound onMessageFound) {
synchronized (this.messages) {
for(Message message : this.messages) {
for (Message message : this.messages) {
if (message.getStatus() == Message.STATUS_WAITING) {
onMessageFound.onMessageFound(message);
}
@ -125,7 +164,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public void findUnreadMessages(OnMessageFound onMessageFound) {
synchronized (this.messages) {
for(Message message : this.messages) {
for (Message message : this.messages) {
if (!message.isRead()) {
onMessageFound.onMessageFound(message);
}
@ -139,7 +178,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
if ((message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE)
&& message.getEncryption() != Message.ENCRYPTION_PGP) {
onMessageFound.onMessageFound(message);
}
}
}
}
}
@ -222,7 +261,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
if (message.getType() != Message.TYPE_IMAGE
&& message.getStatus() == Message.STATUS_UNSEND) {
onMessageFound.onMessageFound(message);
}
}
}
}
}
@ -242,11 +281,11 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public Message findMessageWithRemoteIdAndCounterpart(String id, Jid counterpart, boolean received, boolean carbon) {
synchronized (this.messages) {
for(int i = this.messages.size() - 1; i >= 0; --i) {
for (int i = this.messages.size() - 1; i >= 0; --i) {
Message message = messages.get(i);
if (counterpart.equals(message.getCounterpart())
&& ((message.getStatus() == Message.STATUS_RECEIVED) == received)
&& (carbon == message.isCarbon() || received) ) {
&& (carbon == message.isCarbon() || received)) {
if (id.equals(message.getRemoteMsgId()) && !message.isFileOrImage() && !message.treatAsDownloadable()) {
return message;
} else {
@ -271,7 +310,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public Message findMessageWithRemoteId(String id, Jid counterpart) {
synchronized (this.messages) {
for(Message message : this.messages) {
for (Message message : this.messages) {
if (counterpart.equals(message.getCounterpart())
&& (id.equals(message.getRemoteMsgId()) || id.equals(message.getUuid()))) {
return message;
@ -283,7 +322,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public boolean hasMessageWithCounterpart(Jid counterpart) {
synchronized (this.messages) {
for(Message message : this.messages) {
for (Message message : this.messages) {
if (counterpart.equals(message.getCounterpart())) {
return true;
}
@ -297,7 +336,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
messages.clear();
messages.addAll(this.messages);
}
for(Iterator<Message> iterator = messages.iterator(); iterator.hasNext();) {
for (Iterator<Message> iterator = messages.iterator(); iterator.hasNext(); ) {
if (iterator.next().wasMergedIntoPrevious()) {
iterator.remove();
}
@ -325,19 +364,19 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
}
}
public void setFirstMamReference(String reference) {
this.mFirstMamReference = reference;
}
public String getFirstMamReference() {
return this.mFirstMamReference;
}
public void setLastClearHistory(long time,String reference) {
public void setFirstMamReference(String reference) {
this.mFirstMamReference = reference;
}
public void setLastClearHistory(long time, String reference) {
if (reference != null) {
setAttribute(ATTRIBUTE_LAST_CLEAR_HISTORY, String.valueOf(time) + ":" + reference);
} else {
setAttribute(ATTRIBUTE_LAST_CLEAR_HISTORY, String.valueOf(time));
setAttribute(ATTRIBUTE_LAST_CLEAR_HISTORY, time);
}
}
@ -372,53 +411,25 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
@Override
public int compareTo(@NonNull Conversation another) {
final Message left = getLatestMessage();
final Message right = another.getLatestMessage();
if (left.getTimeSent() > right.getTimeSent()) {
return -1;
} else if (left.getTimeSent() < right.getTimeSent()) {
return 1;
} else {
return 0;
}
return Long.compare(another.getSortableTime(), getSortableTime());
}
public void setDraftMessage(String draftMessage) {
this.draftMessage = draftMessage;
private long getSortableTime() {
Draft draft = getDraft();
long messageTime = getLatestMessage().getTimeSent();
if (draft == null) {
return messageTime;
} else {
return Math.max(messageTime, draft.getTimestamp());
}
}
public String getDraftMessage() {
return draftMessage;
}
public interface OnMessageFound {
void onMessageFound(final Message message);
}
public Conversation(final String name, final Account account, final Jid contactJid,
final int mode) {
this(java.util.UUID.randomUUID().toString(), name, null, account
.getUuid(), contactJid, System.currentTimeMillis(),
STATUS_AVAILABLE, mode, "");
this.account = account;
}
public Conversation(final String uuid, final String name, final String contactUuid,
final String accountUuid, final Jid contactJid, final long created, final int status,
final int mode, final String attributes) {
this.uuid = uuid;
this.name = name;
this.contactUuid = contactUuid;
this.accountUuid = accountUuid;
this.contactJid = contactJid;
this.created = created;
this.status = status;
this.mode = mode;
try {
this.attributes = new JSONObject(attributes == null ? "" : attributes);
} catch (JSONException e) {
this.attributes = new JSONObject();
}
public void setDraftMessage(String draftMessage) {
this.draftMessage = draftMessage;
}
public boolean isRead() {
@ -428,7 +439,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public List<Message> markRead() {
final List<Message> unread = new ArrayList<>();
synchronized (this.messages) {
for(Message message : this.messages) {
for (Message message : this.messages) {
if (!message.isRead()) {
message.markRead();
unread.add(message);
@ -497,14 +508,14 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
return this.account;
}
public Contact getContact() {
return this.account.getRoster().getContact(this.contactJid);
}
public void setAccount(final Account account) {
this.account = account;
}
public Contact getContact() {
return this.account.getRoster().getContact(this.contactJid);
}
@Override
public Jid getJid() {
return this.contactJid;
@ -514,6 +525,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
return this.status;
}
public void setStatus(int status) {
this.status = status;
}
public long getCreated() {
return this.created;
}
@ -532,29 +547,6 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
return values;
}
public static Conversation fromCursor(Cursor cursor) {
Jid jid;
try {
jid = Jid.of(cursor.getString(cursor.getColumnIndex(CONTACTJID)));
} catch (final IllegalArgumentException e) {
// Borked DB..
jid = null;
}
return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)),
cursor.getString(cursor.getColumnIndex(NAME)),
cursor.getString(cursor.getColumnIndex(CONTACT)),
cursor.getString(cursor.getColumnIndex(ACCOUNT)),
jid,
cursor.getLong(cursor.getColumnIndex(CREATED)),
cursor.getInt(cursor.getColumnIndex(STATUS)),
cursor.getInt(cursor.getColumnIndex(MODE)),
cursor.getString(cursor.getColumnIndex(ATTRIBUTES)));
}
public void setStatus(int status) {
this.status = status;
}
public int getMode() {
return this.mode;
}
@ -589,14 +581,14 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
this.contactJid = jid;
}
public void setNextCounterpart(Jid jid) {
this.nextCounterpart = jid;
}
public Jid getNextCounterpart() {
return this.nextCounterpart;
}
public void setNextCounterpart(Jid jid) {
this.nextCounterpart = jid;
}
public int getNextEncryption() {
final int defaultEncryption;
AxolotlService axolotlService = account.getAxolotlService();
@ -605,7 +597,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
} else if (axolotlService != null && axolotlService.isConversationAxolotlCapable(this)) {
defaultEncryption = Message.ENCRYPTION_AXOLOTL;
} else {
defaultEncryption = Message.ENCRYPTION_NONE;
defaultEncryption = Message.ENCRYPTION_NONE;
}
int encryption = this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, defaultEncryption);
if (encryption == Message.ENCRYPTION_OTR) {
@ -624,9 +616,24 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
return nextMessage == null ? "" : nextMessage;
}
public @Nullable
Draft getDraft() {
long timestamp = getLongAttribute(ATTRIBUTE_NEXT_MESSAGE_TIMESTAMP, 0);
if (timestamp > getLatestMessage().getTimeSent()) {
String message = getAttribute(ATTRIBUTE_NEXT_MESSAGE);
if (!TextUtils.isEmpty(message) && timestamp != 0) {
return new Draft(message, timestamp);
}
}
return null;
}
public boolean setNextMessage(String message) {
boolean changed = !getNextMessage().equals(message);
this.setAttribute(ATTRIBUTE_NEXT_MESSAGE, message);
if (changed) {
this.setAttribute(ATTRIBUTE_NEXT_MESSAGE_TIMESTAMP, TextUtils.isEmpty(message) ? 0 : System.currentTimeMillis());
}
return changed;
}
@ -673,18 +680,18 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
final MamReference lastClear = getLastClearHistory();
MamReference lastReceived = new MamReference(0);
synchronized (this.messages) {
for(int i = this.messages.size() - 1; i >= 0; --i) {
for (int i = this.messages.size() - 1; i >= 0; --i) {
final Message message = this.messages.get(i);
if (message.getType() == Message.TYPE_PRIVATE) {
continue; //it's unsafe to use private messages as anchor. They could be coming from user archive
}
if (message.getStatus() == Message.STATUS_RECEIVED || message.isCarbon() || message.getServerMsgId() != null) {
lastReceived = new MamReference(message.getTimeSent(),message.getServerMsgId());
lastReceived = new MamReference(message.getTimeSent(), message.getServerMsgId());
break;
}
}
}
return MamReference.max(lastClear,lastReceived);
return MamReference.max(lastClear, lastReceived);
}
public void setMutedTill(long value) {
@ -699,6 +706,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
return mode == MODE_SINGLE || getBooleanAttribute(ATTRIBUTE_ALWAYS_NOTIFY, Config.ALWAYS_NOTIFY_BY_DEFAULT || isPrivateAndNonAnonymous());
}
private boolean setAttribute(String key, long value) {
return setAttribute(key, Long.toString(value));
}
public boolean setAttribute(String key, String value) {
synchronized (this.attributes) {
try {
@ -712,7 +723,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public boolean setAttribute(String key, List<Jid> jids) {
JSONArray array = new JSONArray();
for(Jid jid : jids) {
for (Jid jid : jids) {
array.put(jid.asBareJid().toString());
}
synchronized (this.attributes) {
@ -798,7 +809,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public void prepend(int offset, Message message) {
synchronized (this.messages) {
this.messages.add(Math.min(offset,this.messages.size()),message);
this.messages.add(Math.min(offset, this.messages.size()), message);
}
}
@ -811,7 +822,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public void expireOldMessages(long timestamp) {
synchronized (this.messages) {
for(ListIterator<Message> iterator = this.messages.listIterator(); iterator.hasNext();) {
for (ListIterator<Message> iterator = this.messages.listIterator(); iterator.hasNext(); ) {
if (iterator.next().getTimeSent() < timestamp) {
iterator.remove();
}
@ -836,7 +847,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
}
private void untieMessages() {
for(Message message : this.messages) {
for (Message message : this.messages) {
message.untie();
}
}
@ -844,7 +855,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public int unreadCount() {
synchronized (this.messages) {
int count = 0;
for(int i = this.messages.size() - 1; i >= 0; --i) {
for (int i = this.messages.size() - 1; i >= 0; --i) {
if (this.messages.get(i).isRead()) {
return count;
}
@ -857,7 +868,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public int receivedMessagesCount() {
int count = 0;
synchronized (this.messages) {
for(Message message : messages) {
for (Message message : messages) {
if (message.getStatus() == Message.STATUS_RECEIVED) {
++count;
}
@ -869,7 +880,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
private int sentMessagesCount() {
int count = 0;
synchronized (this.messages) {
for(Message message : messages) {
for (Message message : messages) {
if (message.getStatus() != Message.STATUS_RECEIVED) {
++count;
}
@ -884,4 +895,26 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
&& !getContact().showInRoster()
&& sentMessagesCount() == 0;
}
public interface OnMessageFound {
void onMessageFound(final Message message);
}
public static class Draft {
private final String message;
private final long timestamp;
private Draft(String message, long timestamp) {
this.message = message;
this.timestamp = timestamp;
}
public long getTimestamp() {
return timestamp;
}
public String getMessage() {
return message;
}
}
}

View file

@ -45,18 +45,45 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
this.activity = activity;
}
private static boolean cancelPotentialWork(Conversation conversation, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
final Conversation oldConversation = bitmapWorkerTask.conversation;
if (oldConversation == null || conversation != oldConversation) {
bitmapWorkerTask.cancel(true);
} else {
return false;
}
}
return true;
}
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
@Override
public View getView(int position, View view, @NonNull ViewGroup parent) {
public @NonNull
View getView(int position, View view, @NonNull ViewGroup parent) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.conversation_list_row,parent, false);
view = inflater.inflate(R.layout.conversation_list_row, parent, false);
}
ViewHolder viewHolder = ViewHolder.get(view);
Conversation conversation = getItem(position);
if (this.activity instanceof XmppActivity) {
int c = Color.get(activity, conversation == selectedConversation ? R.attr.color_background_secondary: R.attr.color_background_primary);
viewHolder.swipeableItem.setBackgroundColor(c);
if (conversation == null) {
return view;
}
int c = Color.get(activity, conversation == selectedConversation ? R.attr.color_background_secondary : R.attr.color_background_primary);
viewHolder.swipeableItem.setBackgroundColor(c);
if (conversation.getMode() == Conversation.MODE_SINGLE || activity.useSubjectToIdentifyConference()) {
CharSequence name = conversation.getName();
if (name instanceof Jid) {
@ -69,7 +96,9 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
}
Message message = conversation.getLatestMessage();
int unreadCount = conversation.unreadCount();
final int unreadCount = conversation.unreadCount();
final boolean isRead = conversation.isRead();
final Conversation.Draft draft = isRead ? conversation.getDraft() : null;
if (unreadCount > 0) {
viewHolder.unreadCount.setVisibility(View.VISIBLE);
viewHolder.unreadCount.setUnreadCount(unreadCount);
@ -77,88 +106,97 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
viewHolder.unreadCount.setVisibility(View.GONE);
}
if (!conversation.isRead()) {
viewHolder.name.setTypeface(null, Typeface.BOLD);
} else {
if (isRead) {
viewHolder.name.setTypeface(null, Typeface.NORMAL);
} else {
viewHolder.name.setTypeface(null, Typeface.BOLD);
}
final boolean fileAvailable = message.getTransferable() == null || message.getTransferable().getStatus() != Transferable.STATUS_DELETED;
final boolean showPreviewText;
if (fileAvailable && (message.isFileOrImage() || message.treatAsDownloadable() || message.isGeoUri())) {
final int imageResource;
if (message.isGeoUri()) {
imageResource = activity.getThemeResource(R.attr.ic_attach_location, R.drawable.ic_attach_location);
showPreviewText = false;
if (draft != null) {
viewHolder.lastMessageIcon.setVisibility(View.GONE);
viewHolder.lastMessage.setText(EmojiWrapper.transform(draft.getMessage()));
viewHolder.sender.setText(R.string.draft);
viewHolder.sender.setVisibility(View.VISIBLE);
viewHolder.lastMessage.setTypeface(null, Typeface.NORMAL);
viewHolder.sender.setTypeface(null, Typeface.ITALIC);
} else {
final boolean fileAvailable = message.getTransferable() == null || message.getTransferable().getStatus() != Transferable.STATUS_DELETED;
final boolean showPreviewText;
if (fileAvailable && (message.isFileOrImage() || message.treatAsDownloadable() || message.isGeoUri())) {
final int imageResource;
if (message.isGeoUri()) {
imageResource = activity.getThemeResource(R.attr.ic_attach_location, R.drawable.ic_attach_location);
showPreviewText = false;
} else {
final String mime = message.getMimeType();
switch (mime == null ? "" : mime.split("/")[0]) {
case "image":
imageResource = activity.getThemeResource(R.attr.ic_attach_photo, R.drawable.ic_attach_photo);
showPreviewText = false;
break;
case "video":
imageResource = activity.getThemeResource(R.attr.ic_attach_videocam, R.drawable.ic_attach_videocam);
showPreviewText = false;
break;
case "audio":
imageResource = activity.getThemeResource(R.attr.ic_attach_record, R.drawable.ic_attach_record);
showPreviewText = false;
break;
default:
imageResource = activity.getThemeResource(R.attr.ic_attach_document, R.drawable.ic_attach_document);
showPreviewText = true;
break;
}
}
viewHolder.lastMessageIcon.setImageResource(imageResource);
viewHolder.lastMessageIcon.setVisibility(View.VISIBLE);
} else {
final String mime = message.getMimeType();
switch (mime == null ? "" : mime.split("/")[0]) {
case "image":
imageResource = activity.getThemeResource(R.attr.ic_attach_photo, R.drawable.ic_attach_photo);
showPreviewText = false;
break;
case "video":
imageResource = activity.getThemeResource(R.attr.ic_attach_videocam, R.drawable.ic_attach_videocam);
showPreviewText = false;
break;
case "audio":
imageResource = activity.getThemeResource(R.attr.ic_attach_record, R.drawable.ic_attach_record);
showPreviewText = false;
break;
default:
imageResource = activity.getThemeResource(R.attr.ic_attach_document, R.drawable.ic_attach_document);
showPreviewText = true;
break;
viewHolder.lastMessageIcon.setVisibility(View.GONE);
showPreviewText = true;
}
final Pair<String, Boolean> preview = UIHelper.getMessagePreview(activity, message);
if (showPreviewText) {
viewHolder.lastMessage.setText(EmojiWrapper.transform(preview.first));
} else {
viewHolder.lastMessageIcon.setContentDescription(preview.first);
}
viewHolder.lastMessage.setVisibility(showPreviewText ? View.VISIBLE : View.GONE);
if (preview.second) {
if (isRead) {
viewHolder.lastMessage.setTypeface(null, Typeface.ITALIC);
viewHolder.sender.setTypeface(null, Typeface.NORMAL);
} else {
viewHolder.lastMessage.setTypeface(null, Typeface.BOLD_ITALIC);
viewHolder.sender.setTypeface(null, Typeface.BOLD);
}
} else {
if (isRead) {
viewHolder.lastMessage.setTypeface(null, Typeface.NORMAL);
viewHolder.sender.setTypeface(null, Typeface.NORMAL);
} else {
viewHolder.lastMessage.setTypeface(null, Typeface.BOLD);
viewHolder.sender.setTypeface(null, Typeface.BOLD);
}
}
viewHolder.lastMessageIcon.setImageResource(imageResource);
viewHolder.lastMessageIcon.setVisibility(View.VISIBLE);
} else {
viewHolder.lastMessageIcon.setVisibility(View.GONE);
showPreviewText = true;
}
final Pair<String,Boolean> preview = UIHelper.getMessagePreview(activity,message);
if (showPreviewText) {
viewHolder.lastMessage.setText(EmojiWrapper.transform(preview.first));
} else {
viewHolder.lastMessageIcon.setContentDescription(preview.first);
}
viewHolder.lastMessage.setVisibility(showPreviewText ? View.VISIBLE : View.GONE);
if (preview.second) {
if (conversation.isRead()) {
viewHolder.lastMessage.setTypeface(null, Typeface.ITALIC);
viewHolder.sender.setTypeface(null, Typeface.NORMAL);
} else {
viewHolder.lastMessage.setTypeface(null,Typeface.BOLD_ITALIC);
viewHolder.sender.setTypeface(null,Typeface.BOLD);
}
} else {
if (conversation.isRead()) {
viewHolder.lastMessage.setTypeface(null,Typeface.NORMAL);
viewHolder.sender.setTypeface(null,Typeface.NORMAL);
} else {
viewHolder.lastMessage.setTypeface(null,Typeface.BOLD);
viewHolder.sender.setTypeface(null,Typeface.BOLD);
}
}
if (message.getStatus() == Message.STATUS_RECEIVED) {
if (conversation.getMode() == Conversation.MODE_MULTI) {
if (message.getStatus() == Message.STATUS_RECEIVED) {
if (conversation.getMode() == Conversation.MODE_MULTI) {
viewHolder.sender.setVisibility(View.VISIBLE);
viewHolder.sender.setText(UIHelper.getMessageDisplayName(message).split("\\s+")[0] + ':');
} else {
viewHolder.sender.setVisibility(View.GONE);
}
} else if (message.getType() != Message.TYPE_STATUS) {
viewHolder.sender.setVisibility(View.VISIBLE);
viewHolder.sender.setText(UIHelper.getMessageDisplayName(message).split("\\s+")[0]+':');
viewHolder.sender.setText(activity.getString(R.string.me) + ':');
} else {
viewHolder.sender.setVisibility(View.GONE);
}
} else if (message.getType() != Message.TYPE_STATUS) {
viewHolder.sender.setVisibility(View.VISIBLE);
viewHolder.sender.setText(activity.getString(R.string.me)+':');
} else {
viewHolder.sender.setVisibility(View.GONE);
}
long muted_till = conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL,0);
long muted_till = conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
if (muted_till == Long.MAX_VALUE) {
viewHolder.notificationIcon.setVisibility(View.VISIBLE);
int ic_notifications_off = activity.getThemeResource(R.attr.icon_notifications_off, R.drawable.ic_notifications_off_black_24dp);
int ic_notifications_off = activity.getThemeResource(R.attr.icon_notifications_off, R.drawable.ic_notifications_off_black_24dp);
viewHolder.notificationIcon.setImageResource(ic_notifications_off);
} else if (muted_till >= System.currentTimeMillis()) {
viewHolder.notificationIcon.setVisibility(View.VISIBLE);
@ -168,11 +206,17 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
viewHolder.notificationIcon.setVisibility(View.GONE);
} else {
viewHolder.notificationIcon.setVisibility(View.VISIBLE);
int ic_notifications_none = activity.getThemeResource(R.attr.icon_notifications_none, R.drawable.ic_notifications_none_black_24dp);
int ic_notifications_none = activity.getThemeResource(R.attr.icon_notifications_none, R.drawable.ic_notifications_none_black_24dp);
viewHolder.notificationIcon.setImageResource(ic_notifications_none);
}
viewHolder.timestamp.setText(UIHelper.readableTimeDifference(activity,conversation.getLatestMessage().getTimeSent()));
long timestamp;
if (draft != null) {
timestamp = draft.getTimestamp();
} else {
timestamp = conversation.getLatestMessage().getTimeSent();
}
viewHolder.timestamp.setText(UIHelper.readableTimeDifference(activity, timestamp));
loadAvatar(conversation, viewHolder.avatar);
return view;
@ -184,6 +228,27 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
super.notifyDataSetChanged();
}
private void loadAvatar(Conversation conversation, ImageView imageView) {
if (cancelPotentialWork(conversation, imageView)) {
final Bitmap bm = activity.avatarService().get(conversation, activity.getPixel(56), true);
if (bm != null) {
cancelPotentialWork(conversation, imageView);
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000);
} else {
imageView.setBackgroundColor(UIHelper.getColorForName(conversation.getName().toString()));
imageView.setImageDrawable(null);
final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
imageView.setImageDrawable(asyncDrawable);
try {
task.execute(conversation);
} catch (final RejectedExecutionException ignored) {
}
}
}
}
public static class ViewHolder {
private View swipeableItem;
private TextView name;
@ -218,6 +283,19 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
}
}
static class AsyncDrawable extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
}
class BitmapWorkerTask extends AsyncTask<Conversation, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private Conversation conversation = null;
@ -243,63 +321,4 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
}
}
}
private void loadAvatar(Conversation conversation, ImageView imageView) {
if (cancelPotentialWork(conversation, imageView)) {
final Bitmap bm = activity.avatarService().get(conversation, activity.getPixel(56), true);
if (bm != null) {
cancelPotentialWork(conversation, imageView);
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000);
} else {
imageView.setBackgroundColor(UIHelper.getColorForName(conversation.getName().toString()));
imageView.setImageDrawable(null);
final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
imageView.setImageDrawable(asyncDrawable);
try {
task.execute(conversation);
} catch (final RejectedExecutionException ignored) {
}
}
}
}
private static boolean cancelPotentialWork(Conversation conversation, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
final Conversation oldConversation = bitmapWorkerTask.conversation;
if (oldConversation == null || conversation != oldConversation) {
bitmapWorkerTask.cancel(true);
} else {
return false;
}
}
return true;
}
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
static class AsyncDrawable extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
}
}

View file

@ -734,4 +734,5 @@
<string name="error_trustkey_hint_mutual">Hint: In some cases this can be fixed by adding each other your contact lists.</string>
<string name="disable_encryption_message">Are you sure you want to disable OMEMO encryption for this conversation?\nThis will allow your server administrator to read your messages, but it might be the only way to communicate with people using outdated clients.</string>
<string name="disable_now">Disable now</string>
<string name="draft">Draft:</string>
</resources>