Improved chat message display

This commit is contained in:
Sylvain Berfini 2019-02-21 17:13:36 +01:00
parent 15ab10b4cb
commit f110e817d5
6 changed files with 119 additions and 118 deletions

View file

@ -76,10 +76,10 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
public final ImageView outgoingImdn; public final ImageView outgoingImdn;
public final TextView messageText; public final TextView messageText;
public final FlexboxLayout pictures; public final FlexboxLayout multiFileContents;
public final RelativeLayout singleFileContent;
public final CheckBox deleteEvent; public final CheckBox delete;
public final CheckBox deleteMessage;
private Context mContext; private Context mContext;
private ChatMessageViewHolderClickListener mListener; private ChatMessageViewHolderClickListener mListener;
@ -111,10 +111,10 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
outgoingImdn = view.findViewById(R.id.imdn); outgoingImdn = view.findViewById(R.id.imdn);
messageText = view.findViewById(R.id.message); messageText = view.findViewById(R.id.message);
pictures = view.findViewById(R.id.pictures); singleFileContent = view.findViewById(R.id.single_content);
multiFileContents = view.findViewById(R.id.multi_content);
deleteEvent = view.findViewById(R.id.delete_event); delete = view.findViewById(R.id.delete_event);
deleteMessage = view.findViewById(R.id.delete_message);
} }
@Override @Override
@ -135,8 +135,8 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
avatarLayout.setVisibility(View.GONE); avatarLayout.setVisibility(View.GONE);
sendInProgress.setVisibility(View.GONE); sendInProgress.setVisibility(View.GONE);
downloadInProgress.setVisibility(View.GONE); downloadInProgress.setVisibility(View.GONE);
pictures.setVisibility(View.GONE); singleFileContent.setVisibility(View.GONE);
pictures.removeAllViews(); multiFileContents.setVisibility(View.GONE);
ChatMessage.State status = message.getState(); ChatMessage.State status = message.getState();
Address remoteSender = message.getFromAddress(); Address remoteSender = message.getFromAddress();
@ -174,7 +174,7 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
// Can't anchor incoming messages, setting this to align max width with LIME icon // Can't anchor incoming messages, setting this to align max width with LIME icon
bubbleLayout.setPadding( bubbleLayout.setPadding(
0, 0, (int) ImageUtils.dpToPixels(LinphoneActivity.instance(), 16), 0); 0, 0, (int) ImageUtils.dpToPixels(LinphoneActivity.instance(), 18), 0);
if (status == ChatMessage.State.InProgress) { if (status == ChatMessage.State.InProgress) {
downloadInProgress.setVisibility(View.VISIBLE); downloadInProgress.setVisibility(View.VISIBLE);
@ -217,87 +217,98 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
} }
} }
if (fileContents.size() > 0) { if (fileContents.size() == 1) {
pictures.setVisibility(View.VISIBLE); singleFileContent.setVisibility(View.VISIBLE);
displayContent(message, fileContents.get(0), singleFileContent, false);
} else if (fileContents.size() > 1) {
multiFileContents.removeAllViews();
multiFileContents.setVisibility(View.VISIBLE);
for (Content c : fileContents) { for (Content c : fileContents) {
View content = View content =
LayoutInflater.from(mContext) LayoutInflater.from(mContext)
.inflate(R.layout.chat_bubble_content, null, false); .inflate(R.layout.chat_bubble_content, null, false);
if (c.isFile() || (c.isFileTransfer() && message.isOutgoing())) { displayContent(message, c, content, true);
// If message is outgoing, even if content
// is file transfer we have the file available
String filePath = c.getFilePath();
View v; multiFileContents.addView(content);
if (FileUtils.isExtensionImage(filePath)) { }
if (fileContents.size() == 1 }
&& mContext.getResources() }
.getBoolean(
R.bool.use_big_pictures_to_preview_images_file_transfers)) { private void displayContent(
v = content.findViewById(R.id.bigImage); final ChatMessage message, Content c, View content, boolean isMultiContent) {
loadBitmap(c.getFilePath(), ((ImageView) v)); Button download = content.findViewById(R.id.download);
} else { download.setVisibility(View.GONE);
v = content.findViewById(R.id.image);
loadBitmap(c.getFilePath(), ((ImageView) v)); if (c.isFile() || (c.isFileTransfer() && message.isOutgoing())) {
} // If message is outgoing, even if content
} else { // is file transfer we have the file available
v = content.findViewById(R.id.file); final String filePath = c.getFilePath();
((TextView) v).setText(FileUtils.getNameFromFilePath(filePath));
} View v;
v.setVisibility(View.VISIBLE); if (FileUtils.isExtensionImage(filePath)) {
v.setTag(c.getFilePath()); if (!isMultiContent
v.setOnClickListener( && mContext.getResources()
new View.OnClickListener() { .getBoolean(
@Override R.bool.use_big_pictures_to_preview_images_file_transfers)) {
public void onClick(View v) { v = content.findViewById(R.id.bigImage);
openFile((String) v.getTag()); loadBitmap(c.getFilePath(), ((ImageView) v));
}
});
} else { } else {
Button download = content.findViewById(R.id.download); v = content.findViewById(R.id.image);
download.setVisibility(View.VISIBLE); loadBitmap(c.getFilePath(), ((ImageView) v));
}
if (mContext.getPackageManager() } else {
.checkPermission( v = content.findViewById(R.id.file);
Manifest.permission.WRITE_EXTERNAL_STORAGE, ((TextView) v).setText(FileUtils.getNameFromFilePath(filePath));
mContext.getPackageName()) }
== PackageManager.PERMISSION_GRANTED) { v.setVisibility(View.VISIBLE);
String filename = c.getName(); v.setOnClickListener(
File file = new File(FileUtils.getStorageDirectory(mContext), filename); new View.OnClickListener() {
@Override
int prefix = 1; public void onClick(View v) {
while (file.exists()) { openFile(filePath);
file =
new File(
FileUtils.getStorageDirectory(mContext),
prefix + "_" + filename);
Log.w(
"File with that name already exists, renamed to "
+ prefix
+ "_"
+ filename);
prefix += 1;
} }
});
} else {
download.setVisibility(View.VISIBLE);
download.setTag(c); if (mContext.getPackageManager()
c.setFilePath(file.getPath()); .checkPermission(
download.setOnClickListener( Manifest.permission.WRITE_EXTERNAL_STORAGE,
new View.OnClickListener() { mContext.getPackageName())
@Override == PackageManager.PERMISSION_GRANTED) {
public void onClick(View v) { String filename = c.getName();
Content c = (Content) v.getTag(); File file = new File(FileUtils.getStorageDirectory(mContext), filename);
message.downloadContent(c);
} int prefix = 1;
}); while (file.exists()) {
} else { file =
Log.w( new File(
"WRITE_EXTERNAL_STORAGE permission not granted, won't be able to store the downloaded file"); FileUtils.getStorageDirectory(mContext),
LinphoneActivity.instance().checkAndRequestExternalStoragePermission(); prefix + "_" + filename);
} Log.w(
"File with that name already exists, renamed to "
+ prefix
+ "_"
+ filename);
prefix += 1;
} }
pictures.addView(content); download.setTag(c);
c.setFilePath(file.getPath());
download.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
Content c = (Content) v.getTag();
message.downloadContent(c);
}
});
} else {
Log.w(
"WRITE_EXTERNAL_STORAGE permission not granted, won't be able to store the downloaded file");
LinphoneActivity.instance().checkAndRequestExternalStoragePermission();
} }
} }
} }

View file

@ -106,30 +106,29 @@ public class ChatMessagesAdapter extends SelectableAdapter<ChatMessageViewHolder
public void onBindViewHolder(@NonNull ChatMessageViewHolder holder, int position) { public void onBindViewHolder(@NonNull ChatMessageViewHolder holder, int position) {
EventLog event = mHistory.get(position); EventLog event = mHistory.get(position);
holder.deleteEvent.setVisibility(View.GONE); holder.delete.setVisibility(View.GONE);
holder.deleteMessage.setVisibility(View.GONE);
holder.eventLayout.setVisibility(View.GONE); holder.eventLayout.setVisibility(View.GONE);
holder.securityEventLayout.setVisibility(View.GONE); holder.securityEventLayout.setVisibility(View.GONE);
holder.rightAnchor.setVisibility(View.GONE); holder.rightAnchor.setVisibility(View.GONE);
holder.bubbleLayout.setVisibility(View.GONE); holder.bubbleLayout.setVisibility(View.GONE);
holder.sendInProgress.setVisibility(View.GONE); holder.sendInProgress.setVisibility(View.GONE);
if (isEditionEnabled()) {
holder.delete.setVisibility(View.VISIBLE);
holder.delete.setChecked(isSelected(position));
holder.delete.setTag(position);
}
if (event.getType() == EventLog.Type.ConferenceChatMessage) { if (event.getType() == EventLog.Type.ConferenceChatMessage) {
ChatMessage message = event.getChatMessage(); ChatMessage message = event.getChatMessage();
if (isEditionEnabled()) {
holder.deleteMessage.setVisibility(View.VISIBLE);
holder.deleteMessage.setChecked(isSelected(position));
holder.deleteMessage.setTag(position);
}
if ((message.isOutgoing() && message.getState() != ChatMessage.State.Displayed) if ((message.isOutgoing() && message.getState() != ChatMessage.State.Displayed)
|| (!message.isOutgoing() && message.isFileTransfer())) { || (!message.isOutgoing() && message.isFileTransfer())) {
if (!mTransientMessages.contains(message)) { if (!mTransientMessages.contains(message)) {
mTransientMessages.add(message); mTransientMessages.add(message);
} }
message.setUserData( // This only works if JAVA object is kept, hence the transient list
holder); // This only works if JAVA object is kept, hence the transient list message.setUserData(holder);
message.addListener(mListener); message.addListener(mListener);
} }
@ -146,12 +145,6 @@ public class ChatMessagesAdapter extends SelectableAdapter<ChatMessageViewHolder
holder.bindMessage(message, contact); holder.bindMessage(message, contact);
changeBackgroundDependingOnPreviousAndNextEvents(message, holder, position); changeBackgroundDependingOnPreviousAndNextEvents(message, holder, position);
} else { // Event is not chat message } else { // Event is not chat message
if (isEditionEnabled()) {
holder.deleteEvent.setVisibility(View.VISIBLE);
holder.deleteEvent.setChecked(isSelected(position));
holder.deleteEvent.setTag(position);
}
Address address = event.getParticipantAddress(); Address address = event.getParticipantAddress();
if (address == null && event.getType() == EventLog.Type.ConferenceSecurityEvent) { if (address == null && event.getType() == EventLog.Type.ConferenceSecurityEvent) {
address = event.getSecurityEventFaultyDeviceAddress(); address = event.getSecurityEventFaultyDeviceAddress();

View file

@ -152,8 +152,7 @@ public class ImdnFragment extends Fragment {
LinphoneContact contact = LinphoneContact contact =
ContactsManager.getInstance().findContactFromAddress(remoteSender); ContactsManager.getInstance().findContactFromAddress(remoteSender);
mBubble.deleteMessage.setVisibility(View.GONE); mBubble.delete.setVisibility(View.GONE);
mBubble.deleteEvent.setVisibility(View.GONE);
mBubble.eventLayout.setVisibility(View.GONE); mBubble.eventLayout.setVisibility(View.GONE);
mBubble.securityEventLayout.setVisibility(View.GONE); mBubble.securityEventLayout.setVisibility(View.GONE);
mBubble.rightAnchor.setVisibility(View.GONE); mBubble.rightAnchor.setVisibility(View.GONE);

View file

@ -194,8 +194,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/top" android:layout_below="@id/top"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_marginTop="6dp" android:layout_marginTop="8dp"
android:layout_marginRight="6dp" android:layout_marginRight="8dp"
android:src="@drawable/security_alert_indicator" /> android:src="@drawable/security_alert_indicator" />
</RelativeLayout> </RelativeLayout>

View file

@ -5,15 +5,15 @@
<View <View
android:id="@+id/rightAnchor" android:id="@+id/rightAnchor"
android:layout_width="1dp" android:layout_width="3dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_alignParentRight="true"/> android:layout_alignParentRight="true"/>
<CheckBox <CheckBox
android:id="@+id/delete_event" android:id="@+id/delete_event"
android:layout_width="30dp" android:layout_width="wrap_content"
android:layout_height="30dp" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:adjustViewBounds="true" android:adjustViewBounds="true"
@ -90,13 +90,24 @@
<com.google.android.flexbox.FlexboxLayout <com.google.android.flexbox.FlexboxLayout
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/pictures" android:id="@+id/multi_content"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="5dp" android:layout_marginLeft="5dp"
android:layout_marginRight="5dp" android:layout_marginRight="5dp"
app:flexWrap="wrap" /> app:flexWrap="wrap" />
<RelativeLayout
android:id="@+id/single_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp">
<include layout="@layout/chat_bubble_content" />
</RelativeLayout>
<org.linphone.views.MultiLineWrapContentWidthTextView <org.linphone.views.MultiLineWrapContentWidthTextView
android:id="@+id/message" android:id="@+id/message"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -140,17 +151,6 @@
android:gravity="right" android:gravity="right"
android:textAppearance="@style/chat_bubble_time_font" /> android:textAppearance="@style/chat_bubble_time_font" />
<CheckBox
android:id="@+id/delete_message"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignTop="@id/background"
android:layout_alignParentRight="true"
android:adjustViewBounds="true"
android:button="@drawable/checkbox"
android:clickable="false"
android:contentDescription="@string/content_description_delete" />
<ImageView <ImageView
android:id="@id/imdn" android:id="@id/imdn"
android:layout_width="10dp" android:layout_width="10dp"

View file

@ -6,8 +6,7 @@
<ImageView <ImageView
android:id="@+id/image" android:id="@+id/image"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="100dp"
android:maxHeight="100dp"
android:layout_margin="5dp" android:layout_margin="5dp"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:visibility="gone" /> android:visibility="gone" />
@ -15,8 +14,7 @@
<ImageView <ImageView
android:id="@+id/bigImage" android:id="@+id/bigImage"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="200dp"
android:maxHeight="300dp"
android:layout_margin="5dp" android:layout_margin="5dp"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:visibility="gone" /> android:visibility="gone" />