Commit aab530bf authored by Grigory Fedorov's avatar Grigory Fedorov

Chat: ListView replaced by RecyclerView. Clicks do not work so far.

parent 4224e9a5
...@@ -8,6 +8,8 @@ import android.content.Context; ...@@ -8,6 +8,8 @@ import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.NavUtils; import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBarActivity; import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
...@@ -24,7 +26,6 @@ import android.view.animation.AnimationUtils; ...@@ -24,7 +26,6 @@ import android.view.animation.AnimationUtils;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.PopupMenu; import android.widget.PopupMenu;
import com.xabber.android.data.Application; import com.xabber.android.data.Application;
...@@ -63,7 +64,8 @@ public class ChatViewerFragment extends Fragment implements AdapterView.OnItemCl ...@@ -63,7 +64,8 @@ public class ChatViewerFragment extends Fragment implements AdapterView.OnItemCl
private static final int MINIMUM_MESSAGES_TO_LOAD = 10; private static final int MINIMUM_MESSAGES_TO_LOAD = 10;
private EditText inputView; private EditText inputView;
private ListView listView;
private RecyclerView recyclerView;
private ChatMessageAdapter chatMessageAdapter; private ChatMessageAdapter chatMessageAdapter;
private boolean skipOnTextChanges; private boolean skipOnTextChanges;
...@@ -156,10 +158,12 @@ public class ChatViewerFragment extends Fragment implements AdapterView.OnItemCl ...@@ -156,10 +158,12 @@ public class ChatViewerFragment extends Fragment implements AdapterView.OnItemCl
chatMessageAdapter = new ChatMessageAdapter(getActivity(), account, user); chatMessageAdapter = new ChatMessageAdapter(getActivity(), account, user);
listView = (ListView) view.findViewById(android.R.id.list); recyclerView = (RecyclerView) view.findViewById(R.id.chat_messages_recycler_view);
listView.setAdapter(chatMessageAdapter); recyclerView.setAdapter(chatMessageAdapter);
listView.setOnItemClickListener(this);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
layoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(layoutManager);
inputView = (EditText) view.findViewById(R.id.chat_input); inputView = (EditText) view.findViewById(R.id.chat_input);
...@@ -369,7 +373,7 @@ public class ChatViewerFragment extends Fragment implements AdapterView.OnItemCl ...@@ -369,7 +373,7 @@ public class ChatViewerFragment extends Fragment implements AdapterView.OnItemCl
super.onCreateContextMenu(menu, view, menuInfo); super.onCreateContextMenu(menu, view, menuInfo);
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
ChatMessageAdapter chatMessageAdapter = (ChatMessageAdapter) listView.getAdapter(); ChatMessageAdapter chatMessageAdapter = (ChatMessageAdapter) recyclerView.getAdapter();
int itemViewType = chatMessageAdapter.getItemViewType(info.position); int itemViewType = chatMessageAdapter.getItemViewType(info.position);
...@@ -388,7 +392,7 @@ public class ChatViewerFragment extends Fragment implements AdapterView.OnItemCl ...@@ -388,7 +392,7 @@ public class ChatViewerFragment extends Fragment implements AdapterView.OnItemCl
@Override @Override
public boolean onContextItemSelected(MenuItem item) { public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
final MessageItem message = (MessageItem) listView.getAdapter().getItem(info.position); final MessageItem message = (MessageItem) chatMessageAdapter.getItem(info.position);
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.action_message_repeat: case R.id.action_message_repeat:
...@@ -453,9 +457,9 @@ public class ChatViewerFragment extends Fragment implements AdapterView.OnItemCl ...@@ -453,9 +457,9 @@ public class ChatViewerFragment extends Fragment implements AdapterView.OnItemCl
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
registerForContextMenu(listView); registerForContextMenu(recyclerView);
listView.showContextMenuForChild(view); recyclerView.showContextMenuForChild(view);
unregisterForContextMenu(listView); unregisterForContextMenu(recyclerView);
} }
@Override @Override
......
...@@ -15,13 +15,14 @@ ...@@ -15,13 +15,14 @@
package com.xabber.android.ui.adapter; package com.xabber.android.ui.adapter;
import android.app.Activity; import android.app.Activity;
import android.support.v7.widget.RecyclerView;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import android.text.style.CharacterStyle; import android.text.style.CharacterStyle;
import android.text.style.TextAppearanceSpan; import android.text.style.TextAppearanceSpan;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
...@@ -45,16 +46,8 @@ import java.util.Collections; ...@@ -45,16 +46,8 @@ import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
/** public class ChatMessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements UpdatableAdapter {
* Adapter for the list of messages in the chat.
*
* @author alexander.ivanov
*/
public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter {
private static final int VIEW_TYPE_COUNT = 5;
private static final int VIEW_TYPE_EMPTY = 0;
private static final int VIEW_TYPE_HINT = 1; private static final int VIEW_TYPE_HINT = 1;
public static final int VIEW_TYPE_INCOMING_MESSAGE = 2; public static final int VIEW_TYPE_INCOMING_MESSAGE = 2;
public static final int VIEW_TYPE_OUTGOING_MESSAGE = 3; public static final int VIEW_TYPE_OUTGOING_MESSAGE = 3;
...@@ -81,6 +74,58 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter ...@@ -81,6 +74,58 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter
*/ */
private String hint; private String hint;
public static class HintMessage extends RecyclerView.ViewHolder {
public TextView info;
public HintMessage(View itemView) {
super(itemView);
info = (TextView) itemView.findViewById(R.id.info);
}
}
public static class ActionMessage extends RecyclerView.ViewHolder {
public TextView actionMessage;
public ActionMessage(View itemView) {
super(itemView);
actionMessage = (TextView) itemView.findViewById(R.id.action_message_text);
}
}
public static abstract class Message extends RecyclerView.ViewHolder {
public TextView messageText;
public TextView messageTime;
public Message(View itemView) {
super(itemView);
messageText = (TextView) itemView.findViewById(R.id.message_text);
messageTime = (TextView) itemView.findViewById(R.id.message_time);
}
}
public static class IncomingMessage extends Message {
public ImageView avatar;
public IncomingMessage(View itemView) {
super(itemView);
avatar = (ImageView) itemView.findViewById(R.id.avatar);
}
}
public static class OutgoingMessage extends Message {
public ImageView statusIcon;
public OutgoingMessage(View itemView) {
super(itemView);
statusIcon = (ImageView) itemView.findViewById(R.id.message_status_icon);
}
}
public ChatMessageAdapter(Activity activity, String account, String user) { public ChatMessageAdapter(Activity activity, String account, String user) {
this.activity = activity; this.activity = activity;
messages = Collections.emptyList(); messages = Collections.emptyList();
...@@ -99,11 +144,64 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter ...@@ -99,11 +144,64 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter
} }
@Override @Override
public int getCount() { public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return messages.size() + 1; switch (viewType) {
case VIEW_TYPE_HINT:
return new HintMessage(LayoutInflater.from(parent.getContext())
.inflate(R.layout.chat_viewer_info, parent, false));
case VIEW_TYPE_ACTION_MESSAGE:
return new ActionMessage(LayoutInflater.from(parent.getContext())
.inflate(R.layout.chat_viewer_action_message, parent, false));
case VIEW_TYPE_INCOMING_MESSAGE:
return new IncomingMessage(LayoutInflater.from(parent.getContext())
.inflate(R.layout.chat_viewer_incoming_message, parent, false));
case VIEW_TYPE_OUTGOING_MESSAGE:
return new OutgoingMessage(LayoutInflater.from(parent.getContext())
.inflate(R.layout.chat_viewer_outgoing_message, parent, false));
default:
return null;
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final int viewType = getItemViewType(position);
MessageItem messageItem = (MessageItem) getItem(position);
switch (viewType) {
case VIEW_TYPE_HINT:
((HintMessage)holder).info.setText(hint);
break;
case VIEW_TYPE_ACTION_MESSAGE:
ChatAction action = messageItem.getAction();
String time = StringUtils.getSmartTimeText(activity, messageItem.getTimestamp());
((ActionMessage)holder).actionMessage.setText(time + ": "
+ action.getText(activity, messageItem.getResource(), messageItem.getSpannable().toString()));
break;
case VIEW_TYPE_INCOMING_MESSAGE:
setUpMessage(messageItem, (Message) holder);
setUpAvatar(messageItem, (IncomingMessage) holder);
break;
case VIEW_TYPE_OUTGOING_MESSAGE:
setUpMessage(messageItem, (Message) holder);
setStatusIcon(messageItem, (OutgoingMessage) holder);
break;
}
} }
@Override @Override
public int getItemCount() {
return messages.size() + 1;
}
public Object getItem(int position) { public Object getItem(int position) {
if (position < messages.size()) { if (position < messages.size()) {
return messages.get(position); return messages.get(position);
...@@ -117,15 +215,10 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter ...@@ -117,15 +215,10 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter
return position; return position;
} }
@Override
public int getViewTypeCount() {
return VIEW_TYPE_COUNT;
}
@Override @Override
public int getItemViewType(int position) { public int getItemViewType(int position) {
if (position >= messages.size()) { if (position >= messages.size()) {
return hint == null ? VIEW_TYPE_EMPTY : VIEW_TYPE_HINT; return VIEW_TYPE_HINT;
} }
MessageItem messageItem = (MessageItem) getItem(position); MessageItem messageItem = (MessageItem) getItem(position);
...@@ -136,79 +229,10 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter ...@@ -136,79 +229,10 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter
return messageItem.isIncoming() ? VIEW_TYPE_INCOMING_MESSAGE : VIEW_TYPE_OUTGOING_MESSAGE; return messageItem.isIncoming() ? VIEW_TYPE_INCOMING_MESSAGE : VIEW_TYPE_OUTGOING_MESSAGE;
} }
@Override private void setUpMessage(MessageItem messageItem, Message message) {
public View getView(int position, View convertView, ViewGroup parent) {
final int type = getItemViewType(position);
if (type == VIEW_TYPE_EMPTY) {
if (convertView == null) {
return activity.getLayoutInflater().inflate(R.layout.chat_viewer_empty, parent, false);
} else {
return convertView;
}
}
if (type == VIEW_TYPE_HINT) {
View view = convertView;
if (convertView == null) {
view = activity.getLayoutInflater().inflate(R.layout.chat_viewer_info, parent, false);
}
TextView textView = ((TextView) view.findViewById(R.id.info));
textView.setText(hint);
textView.setTextAppearance(activity, R.style.ChatInfo_Warning);
return view;
}
MessageItem messageItem = (MessageItem) getItem(position);
if (type == VIEW_TYPE_ACTION_MESSAGE) {
View view = convertView;
if (convertView == null) {
view = activity.getLayoutInflater().inflate(R.layout.chat_viewer_action_message, parent, false);
}
ChatAction action = messageItem.getAction();
String time = StringUtils.getSmartTimeText(activity, messageItem.getTimestamp());
((TextView)view.findViewById(R.id.action_message_text)).setText(time + ": "
+ action.getText(activity, messageItem.getResource(), messageItem.getSpannable().toString()));
return view;
}
View view = convertView;
if (convertView == null) {
final int layoutId;
if (type == VIEW_TYPE_INCOMING_MESSAGE) {
layoutId = R.layout.chat_viewer_incoming_message;
} else if (type == VIEW_TYPE_OUTGOING_MESSAGE) {
layoutId = R.layout.chat_viewer_outgoing_message;
} else {
throw new IllegalStateException();
}
view = activity.getLayoutInflater().inflate(layoutId, parent, false);
}
setUpMessageView(messageItem, view);
return view;
}
private void setUpMessageView(MessageItem messageItem, View view) {
final boolean incoming = messageItem.isIncoming();
SpannableStringBuilder builder = new SpannableStringBuilder(); SpannableStringBuilder builder = new SpannableStringBuilder();
final String resource = messageItem.getResource(); final String resource = messageItem.getResource();
if (!incoming) {
setStatusIcon(messageItem, view);
}
if (isMUC) { if (isMUC) {
append(builder, resource, new TextAppearanceSpan(activity, R.style.ChatHeader_Time)); append(builder, resource, new TextAppearanceSpan(activity, R.style.ChatHeader_Time));
append(builder, divider, new TextAppearanceSpan(activity, R.style.ChatHeader)); append(builder, divider, new TextAppearanceSpan(activity, R.style.ChatHeader));
...@@ -230,29 +254,23 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter ...@@ -230,29 +254,23 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter
append(builder, text, new TextAppearanceSpan(activity, R.style.ChatRead)); append(builder, text, new TextAppearanceSpan(activity, R.style.ChatRead));
} }
TextView textView = (TextView) view.findViewById(R.id.message_text); message.messageText.setTextAppearance(activity, appearanceStyle);
textView.setTextAppearance(activity, appearanceStyle); message.messageText.setText(builder);
textView.setText(builder); message.messageText.getBackground().setLevel(AccountManager.getInstance().getColorLevel(account));
textView.getBackground().setLevel(AccountManager.getInstance().getColorLevel(account));
String time = StringUtils.getSmartTimeText(activity, messageItem.getTimestamp()); String time = StringUtils.getSmartTimeText(activity, messageItem.getTimestamp());
if (delayTimestamp != null) { if (delayTimestamp != null) {
String delay = activity.getString(incoming ? R.string.chat_delay : R.string.chat_typed, String delay = activity.getString(messageItem.isIncoming() ? R.string.chat_delay : R.string.chat_typed,
StringUtils.getSmartTimeText(activity, delayTimestamp)); StringUtils.getSmartTimeText(activity, delayTimestamp));
time += " (" + delay + ")"; time += " (" + delay + ")";
} }
((TextView)view.findViewById(R.id.message_time)).setText(time); message.messageTime.setText(time);
if (incoming) {
setUpAvatar(messageItem, view);
}
} }
private void setStatusIcon(MessageItem messageItem, View view) { private void setStatusIcon(MessageItem messageItem, OutgoingMessage message) {
ImageView messageStatusIcon = (ImageView) view.findViewById(R.id.message_status_icon); message.statusIcon.setVisibility(View.VISIBLE);
messageStatusIcon.setVisibility(View.VISIBLE);
int messageIcon = R.drawable.ic_message_delivered_18dp; int messageIcon = R.drawable.ic_message_delivered_18dp;
if (messageItem.isError()) { if (messageItem.isError()) {
...@@ -260,10 +278,10 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter ...@@ -260,10 +278,10 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter
} else if (!messageItem.isSent()) { } else if (!messageItem.isSent()) {
messageIcon = R.drawable.ic_message_not_sent_18dp; messageIcon = R.drawable.ic_message_not_sent_18dp;
} else if (!messageItem.isDelivered()) { } else if (!messageItem.isDelivered()) {
messageStatusIcon.setVisibility(View.INVISIBLE); message.statusIcon.setVisibility(View.INVISIBLE);
} }
messageStatusIcon.setImageResource(messageIcon); message.statusIcon.setImageResource(messageIcon);
} }
private void append(SpannableStringBuilder builder, CharSequence text, CharacterStyle span) { private void append(SpannableStringBuilder builder, CharSequence text, CharacterStyle span) {
...@@ -272,30 +290,28 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter ...@@ -272,30 +290,28 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter
builder.setSpan(span, start, start + text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); builder.setSpan(span, start, start + text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
private void setUpAvatar(MessageItem messageItem, View view) { private void setUpAvatar(MessageItem messageItem, IncomingMessage message) {
ImageView avatarView = (ImageView) view.findViewById(R.id.avatar);
if (SettingsManager.chatsShowAvatars()) { if (SettingsManager.chatsShowAvatars()) {
final String account = messageItem.getChat().getAccount(); final String account = messageItem.getChat().getAccount();
final String user = messageItem.getChat().getUser(); final String user = messageItem.getChat().getUser();
final String resource = messageItem.getResource(); final String resource = messageItem.getResource();
avatarView.setVisibility(View.VISIBLE); message.avatar.setVisibility(View.VISIBLE);
if ((isMUC && MUCManager.getInstance().getNickname(account, user).equalsIgnoreCase(resource))) { if ((isMUC && MUCManager.getInstance().getNickname(account, user).equalsIgnoreCase(resource))) {
avatarView.setImageDrawable(AvatarManager.getInstance().getAccountAvatar(account)); message.avatar.setImageDrawable(AvatarManager.getInstance().getAccountAvatar(account));
} else { } else {
if (isMUC) { if (isMUC) {
if ("".equals(resource)) { if ("".equals(resource)) {
avatarView.setImageDrawable(AvatarManager.getInstance().getRoomAvatar(user)); message.avatar.setImageDrawable(AvatarManager.getInstance().getRoomAvatar(user));
} else { } else {
avatarView.setImageDrawable(AvatarManager.getInstance().getOccupantAvatar(user + "/" + resource)); message.avatar.setImageDrawable(AvatarManager.getInstance().getOccupantAvatar(user + "/" + resource));
} }
} else { } else {
avatarView.setImageDrawable(AvatarManager.getInstance().getUserAvatar(user)); message.avatar.setImageDrawable(AvatarManager.getInstance().getUserAvatar(user));
} }
} }
} else { } else {
avatarView.setVisibility(View.GONE); message.avatar.setVisibility(View.GONE);
} }
} }
......
...@@ -38,12 +38,12 @@ ...@@ -38,12 +38,12 @@
</android.support.v7.widget.Toolbar> </android.support.v7.widget.Toolbar>
<ListView <android.support.v7.widget.RecyclerView
android:id="@android:id/list" android:id="@+id/chat_messages_recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:scrollbars="vertical"
android:stackFromBottom="true"
android:transcriptMode="normal" android:transcriptMode="normal"
android:smoothScrollbar="false" android:smoothScrollbar="false"
android:divider="@null" android:divider="@null"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment