From b8718ac71c572cb7d9fecc2c1c149b563ef1c354 Mon Sep 17 00:00:00 2001 From: Christian Schneppe Date: Sun, 8 Apr 2018 21:40:15 +0200 Subject: show message draft in conversation overview --- .../de/pixart/messenger/entities/Conversation.java | 213 ++++++++++++--------- .../messenger/ui/adapter/ConversationAdapter.java | 189 ++++++++++-------- 2 files changed, 226 insertions(+), 176 deletions(-) (limited to 'src/main/java/de') diff --git a/src/main/java/de/pixart/messenger/entities/Conversation.java b/src/main/java/de/pixart/messenger/entities/Conversation.java index 3f66f93b6..8e8d2a28a 100644 --- a/src/main/java/de/pixart/messenger/entities/Conversation.java +++ b/src/main/java/de/pixart/messenger/entities/Conversation.java @@ -3,6 +3,8 @@ package de.pixart.messenger.entities; import android.content.ContentValues; import android.database.Cursor; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; import net.java.otr4j.OtrException; import net.java.otr4j.crypto.OtrCryptoException; @@ -26,7 +28,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import de.pixart.messenger.Config; import de.pixart.messenger.crypto.PgpDecryptionService; import de.pixart.messenger.crypto.axolotl.AxolotlService; -import de.pixart.messenger.services.XmppConnectionService; import de.pixart.messenger.xmpp.chatstate.ChatState; import de.pixart.messenger.xmpp.mam.MamReference; import rocks.xmpp.addr.Jid; @@ -50,14 +51,17 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl public static final String MODE = "mode"; public static final String ATTRIBUTES = "attributes"; - private static final String ATTRIBUTE_NEXT_ENCRYPTION = "next_encryption"; - static final String ATTRIBUTE_MUC_PASSWORD = "muc_password"; public static final String ATTRIBUTE_MUTED_TILL = "muted_till"; public static final String ATTRIBUTE_ALWAYS_NOTIFY = "always_notify"; - private static final String ATTRIBUTE_CRYPTO_TARGETS = "crypto_targets"; public static final String ATTRIBUTE_LAST_CLEAR_HISTORY = "last_clear_history"; - public static final String ATTRIBUTE_NEXT_MESSAGE = "next_message"; - + 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 messages = new ArrayList<>(); + public AtomicBoolean messagesLoaded = new AtomicBoolean(true); + protected Account account = null; private String draftMessage; private String name; private String contactUuid; @@ -66,32 +70,64 @@ 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 messages = new ArrayList<>(); - protected Account account = null; - private transient SessionImpl otrSession; - private transient String otrFingerprint = null; private Smp mSmp = new Smp(); - private transient MucOptions mucOptions = null; - private byte[] symmetricKey; - private boolean messagesLeftOnServer = true; private ChatState mOutgoingChatState = Config.DEFAULT_CHATSTATE; private ChatState mIncomingChatState = Config.DEFAULT_CHATSTATE; private String mLastReceivedOtrMessageId = null; private String mFirstMamReference = null; private Message correctingMessage; - public AtomicBoolean messagesLoaded = new AtomicBoolean(true); - XmppConnectionService mXmppConnectionService; + 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; @@ -101,7 +137,6 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl this.messagesLeftOnServer = value; } - public Message getFirstUnreadMessage() { Message first = null; synchronized (this.messages) { @@ -361,19 +396,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 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); } } @@ -408,14 +443,16 @@ 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; + return Long.compare(another.getSortableTime(), getSortableTime()); + } + + private long getSortableTime() { + Draft draft = getDraft(); + long messageTime = getLatestMessage().getTimeSent(); + if (draft == null) { + return messageTime; } else { - return 0; + return Math.max(messageTime, draft.getTimestamp()); } } @@ -427,36 +464,6 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl this.draftMessage = 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 boolean isRead() { return (this.messages.size() == 0) || this.messages.get(this.messages.size() - 1).isRead(); } @@ -555,14 +562,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; @@ -572,6 +579,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; } @@ -590,29 +601,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; } @@ -751,14 +739,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(); @@ -792,9 +780,24 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl return smp().status == Smp.STATUS_CONTACT_REQUESTED; } + 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; } @@ -875,6 +878,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 { @@ -1076,6 +1083,28 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl && 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; + } + } + public class Smp { public static final int STATUS_NONE = 0; public static final int STATUS_CONTACT_REQUESTED = 1; diff --git a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java index 078efdb60..249aa9cb4 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java @@ -74,17 +74,19 @@ public class ConversationAdapter extends ArrayAdapter { } @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); } 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) { @@ -97,118 +99,137 @@ public class ConversationAdapter extends ArrayAdapter { } Message message = conversation.getLatestMessage(); - int unreadcount = conversation.unreadCount(); - int failedcount = conversation.failedCount(); + final int failedCount = conversation.failedCount(); + final int unreadCount = conversation.unreadCount(); + final boolean isRead = conversation.isRead(); + final Conversation.Draft draft = isRead ? conversation.getDraft() : null; viewHolder.receivedStatus.setVisibility(View.GONE); viewHolder.readStatus.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); } - if (unreadcount > 0) { + if (unreadCount > 0) { viewHolder.unreadCount.setVisibility(View.VISIBLE); - viewHolder.unreadCount.setText(unreadcount > 99 ? "\u221E" : String.valueOf(unreadcount)); + viewHolder.unreadCount.setText(unreadCount > 99 ? "\u221E" : String.valueOf(unreadCount)); } else { viewHolder.unreadCount.setVisibility(View.GONE); } - if (failedcount > 0) { + if (failedCount > 0) { viewHolder.failedCount.setVisibility(View.VISIBLE); - viewHolder.failedCount.setText(failedcount > 99 ? "\u221E" : String.valueOf(failedcount)); + viewHolder.failedCount.setText(failedCount > 99 ? "\u221E" : String.valueOf(failedCount)); } else { viewHolder.failedCount.setVisibility(View.GONE); } - 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_video, R.drawable.ic_attach_video); - 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 { + if (draft != null) { viewHolder.lastMessageIcon.setVisibility(View.GONE); - showPreviewText = true; - } - final Pair preview = UIHelper.getMessagePreview(activity, message); - if (showPreviewText) { - viewHolder.lastMessage.setText(EmojiWrapper.transform(preview.first)); + 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 { - 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); + 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_video, R.drawable.ic_attach_video); + 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 { - viewHolder.lastMessage.setTypeface(null, Typeface.BOLD_ITALIC); - viewHolder.sender.setTypeface(null, Typeface.BOLD); + viewHolder.lastMessageIcon.setVisibility(View.GONE); + showPreviewText = true; } - } else { - if (conversation.isRead()) { - viewHolder.lastMessage.setTypeface(null, Typeface.NORMAL); - viewHolder.sender.setTypeface(null, Typeface.NORMAL); + final Pair preview = UIHelper.getMessagePreview(activity, message); + if (showPreviewText) { + viewHolder.lastMessage.setText(EmojiWrapper.transform(preview.first)); } else { - viewHolder.lastMessage.setTypeface(null, Typeface.BOLD); - viewHolder.sender.setTypeface(null, Typeface.BOLD); + viewHolder.lastMessageIcon.setContentDescription(preview.first); } - } - if (message.getStatus() == Message.STATUS_RECEIVED) { - if (conversation.getMode() == Conversation.MODE_MULTI) { + 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); + } + } + 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); if (muted_till == Long.MAX_VALUE) { - viewHolder.notificationIcon.setVisibility(View.VISIBLE); - viewHolder.notificationIcon.setImageResource(R.drawable.ic_notifications_off_grey600_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); - viewHolder.notificationIcon.setImageResource(R.drawable.ic_notifications_paused_grey600_24dp); + int ic_notifications_paused = activity.getThemeResource(R.attr.icon_notifications_paused, R.drawable.ic_notifications_paused_black_24dp); + viewHolder.notificationIcon.setImageResource(ic_notifications_paused); } else if (conversation.alwaysNotify()) { viewHolder.notificationIcon.setVisibility(View.GONE); } else { viewHolder.notificationIcon.setVisibility(View.VISIBLE); - viewHolder.notificationIcon.setImageResource(R.drawable.ic_notifications_none_grey600_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); if (conversation.getMode() == Conversation.MODE_SINGLE && ShowPresenceColoredNames()) { @@ -283,6 +304,12 @@ public class ConversationAdapter extends ArrayAdapter { return view; } + @Override + public void notifyDataSetChanged() { + this.selectedConversation = ConversationFragment.getConversation(activity); + super.notifyDataSetChanged(); + } + private void loadAvatar(Conversation conversation, ImageView imageView) { if (cancelPotentialWork(conversation, imageView)) { final Bitmap bm = activity.avatarService().get(conversation, activity.getPixel(56), true); @@ -304,12 +331,6 @@ public class ConversationAdapter extends ArrayAdapter { } } - @Override - public void notifyDataSetChanged() { - this.selectedConversation = ConversationFragment.getConversation(activity); - super.notifyDataSetChanged(); - } - public static class ViewHolder { private View swipeableItem; private TextView name; -- cgit v1.2.3