From 45903bac6737e2e84d083ee3b4e93b65d4f1b3f5 Mon Sep 17 00:00:00 2001
From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
Date: Wed, 8 Jan 2020 18:04:19 -0500
Subject: [PATCH] conversations: touch-ups for image/file/audio/video
 interactions

Change-Id: I3cc101982ab9bfaf36f7ba7a96f34dc80305f44f
---
 .../cx/ring/adapters/ConversationAdapter.java | 311 +++++++++---------
 .../cx/ring/views/ConversationViewHolder.java | 118 ++++---
 .../src/main/res/layout/item_conv_audio.xml   |  94 ------
 .../main/res/layout/item_conv_audio_me.xml    | 127 +++++++
 .../main/res/layout/item_conv_audio_peer.xml  | 122 +++++++
 .../src/main/res/layout/item_conv_file.xml    | 131 --------
 .../src/main/res/layout/item_conv_file_me.xml | 166 ++++++++++
 .../main/res/layout/item_conv_file_peer.xml   | 162 +++++++++
 .../src/main/res/layout/item_conv_image.xml   |  61 ----
 .../main/res/layout/item_conv_image_me.xml    | 104 ++++++
 .../main/res/layout/item_conv_image_peer.xml  |  94 ++++++
 .../main/res/layout/item_conv_msg_peer.xml    |   2 +-
 ..._conv_video.xml => item_conv_video_me.xml} |  56 +++-
 .../main/res/layout/item_conv_video_peer.xml  |  96 ++++++
 .../main/java/cx/ring/model/DataTransfer.java |   4 +-
 15 files changed, 1150 insertions(+), 498 deletions(-)
 delete mode 100644 ring-android/app/src/main/res/layout/item_conv_audio.xml
 create mode 100644 ring-android/app/src/main/res/layout/item_conv_audio_me.xml
 create mode 100644 ring-android/app/src/main/res/layout/item_conv_audio_peer.xml
 delete mode 100644 ring-android/app/src/main/res/layout/item_conv_file.xml
 create mode 100644 ring-android/app/src/main/res/layout/item_conv_file_me.xml
 create mode 100644 ring-android/app/src/main/res/layout/item_conv_file_peer.xml
 delete mode 100644 ring-android/app/src/main/res/layout/item_conv_image.xml
 create mode 100644 ring-android/app/src/main/res/layout/item_conv_image_me.xml
 create mode 100644 ring-android/app/src/main/res/layout/item_conv_image_peer.xml
 rename ring-android/app/src/main/res/layout/{item_conv_video.xml => item_conv_video_me.xml} (60%)
 create mode 100644 ring-android/app/src/main/res/layout/item_conv_video_peer.xml

diff --git a/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.java b/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.java
index 079be396d..ac900454f 100644
--- a/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.java
+++ b/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.java
@@ -35,7 +35,6 @@ import android.text.format.DateUtils;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.ContextMenu;
-import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -45,7 +44,6 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -65,6 +63,7 @@ import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.TimeUnit;
 
 import cx.ring.R;
 import cx.ring.client.MediaViewerActivity;
@@ -86,8 +85,10 @@ import cx.ring.utils.GlideApp;
 import cx.ring.utils.GlideOptions;
 import cx.ring.utils.ResourceMapper;
 import cx.ring.utils.StringUtils;
-import cx.ring.utils.UiUpdater;
 import cx.ring.views.ConversationViewHolder;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
 
 import static androidx.core.content.FileProvider.getUriForFile;
 
@@ -96,8 +97,6 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
 
     private final ArrayList<Interaction> mInteractions = new ArrayList<>();
 
-    private final long TIMESTAMP_UPDATE_INTERVAL = 10 * DateUtils.SECOND_IN_MILLIS;
-
     private final ConversationPresenter presenter;
     private final ConversationFragment conversationFragment;
     private final int hPadding;
@@ -109,6 +108,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
 
     private int expandedItemPosition = -1;
     private int lastDeliveredPosition = -1;
+    private Observable<Long> timestampUpdateTimer;
 
     private static int[] msgBGLayouts = new int[] {
             R.drawable.textmsg_bg_out_first,
@@ -129,11 +129,16 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
         hPadding = res.getDimensionPixelSize(R.dimen.padding_medium);
         vPadding = res.getDimensionPixelSize(R.dimen.padding_small);
         mPictureMaxSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, res.getDisplayMetrics());
-        int corner = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, res.getDisplayMetrics());
+        int corner = (int) res.getDimension(R.dimen.conversation_message_radius);
         PICTURE_OPTIONS = new GlideOptions()
-                .override(mPictureMaxSize)
                 .transform(new CenterInside())
+                .fitCenter()
+                .override(mPictureMaxSize)
                 .transform(new RoundedCorners(corner));
+        timestampUpdateTimer = Observable.interval(10, TimeUnit.SECONDS)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .startWith(0L);
     }
 
     /**
@@ -215,30 +220,29 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
 
         if (interaction != null) {
             switch (interaction.getType()) {
+                case CONTACT:
+                    return MessageType.CONTACT_EVENT.ordinal();
+                case CALL:
+                    return MessageType.CALL_INFORMATION.ordinal();
                 case TEXT:
                     if (interaction.isIncoming()) {
                         return MessageType.INCOMING_TEXT_MESSAGE.ordinal();
                     } else {
                         return MessageType.OUTGOING_TEXT_MESSAGE.ordinal();
                     }
-                case CALL:
-                    return MessageType.CALL_INFORMATION.ordinal();
                 case DATA_TRANSFER:
                     DataTransfer file = (DataTransfer) interaction;
+                    int out = interaction.isIncoming() ? 0 : 4;
                     if (file.isComplete()) {
                         if (file.isPicture()) {
-                            return MessageType.IMAGE.ordinal();
+                            return MessageType.INCOMING_IMAGE.ordinal() + out;
                         } else if (file.isAudio()) {
-                            return MessageType.AUDIO.ordinal();
+                            return MessageType.INCOMING_AUDIO.ordinal() + out;
                         } else if (file.isVideo()) {
-                            return MessageType.VIDEO.ordinal();
-                        } else {
-                            return MessageType.FILE_TRANSFER.ordinal();
+                            return MessageType.INCOMING_VIDEO.ordinal() + out;
                         }
-                    } else
-                        return MessageType.FILE_TRANSFER.ordinal();
-                case CONTACT:
-                    return MessageType.CONTACT_EVENT.ordinal();
+                    }
+                    return out;
             }
         }
         return MessageType.CALL_INFORMATION.ordinal();
@@ -258,6 +262,8 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
         if (interaction == null)
             return;
 
+        conversationViewHolder.compositeDisposable.clear();
+
         if (interaction.getType() == (InteractionType.TEXT)) {
             configureForTextMessage(conversationViewHolder, interaction, position);
         } else if (interaction.getType() == (InteractionType.CALL)) {
@@ -272,11 +278,8 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
     @Override
     public void onViewRecycled(@NonNull ConversationViewHolder holder) {
         holder.itemView.setOnLongClickListener(null);
-        if (holder.mPhoto != null)
-            holder.mPhoto.setOnLongClickListener(null);
-        if (holder.updater != null) {
-            holder.updater.stop();
-            holder.updater = null;
+        if (holder.mImage != null) {
+            holder.mImage.setOnLongClickListener(null);
         }
         if (holder.video != null) {
             holder.video.setOnClickListener(null);
@@ -301,6 +304,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
             holder.mMsgDetailTxt.setVisibility(View.GONE);
             expandedItemPosition = -1;
         }
+        holder.compositeDisposable.clear();
         super.onViewRecycled(holder);
     }
 
@@ -376,36 +380,74 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
         File path = presenter.getDeviceRuntimeService().getConversationPath(file.getPeerId(), file.getStoragePath());
         file.setSize(path.length());
 
-        String timeSeparationString = timestampToDetailString(viewHolder.itemView.getContext(), file.getTimestamp());
-        if (file.getStatus() == InteractionStatus.TRANSFER_FINISHED) {
-            boolean separateByDetails = shouldSeparateByDetails(interaction, position);
-            if (separateByDetails) {
-                viewHolder.mMsgDetailTxt.setVisibility(View.VISIBLE);
+        String timeString = timestampToDetailString(viewHolder.itemView.getContext(), file.getTimestamp());
+        viewHolder.compositeDisposable.add(timestampUpdateTimer.subscribe(t -> {
+            if (file.getStatus() == InteractionStatus.TRANSFER_FINISHED) {
                 viewHolder.mMsgDetailTxt.setText(String.format("%s - %s",
-                        timeSeparationString, FileUtils.readableFileSize(file.getTotalSize())));
-            } else
-                viewHolder.mMsgDetailTxt.setVisibility(View.GONE);
-        } else {
-            viewHolder.mMsgDetailTxt.setVisibility(View.VISIBLE);
-            viewHolder.mMsgDetailTxt.setText(String.format("%s - %s - %s",
-                    timeSeparationString, FileUtils.readableFileSize(file.getTotalSize()),
-                    ResourceMapper.getReadableFileTransferStatus(conversationFragment.getActivity(), file.getStatus())));
-        }
+                        timeString, FileUtils.readableFileSize(file.getTotalSize())));
+            } else {
+                viewHolder.mMsgDetailTxt.setText(String.format("%s - %s - %s",
+                        timeString, FileUtils.readableFileSize(file.getTotalSize()),
+                        ResourceMapper.getReadableFileTransferStatus(conversationFragment.getActivity(), file.getStatus())));
+            }
+        }));
 
-        MessageType type;
+        TransferMsgType type;
         if (!file.isComplete()) {
-            type = MessageType.FILE_TRANSFER;
+            type = TransferMsgType.FILE;
         } else if (file.isPicture()) {
-            type = MessageType.IMAGE;
+            type = TransferMsgType.IMAGE;
         } else if (file.isAudio()) {
-            type = MessageType.AUDIO;
+            type = TransferMsgType.AUDIO;
         } else if (file.isVideo()) {
-            type = MessageType.VIDEO;
+            type = TransferMsgType.VIDEO;
+        } else {
+            type = TransferMsgType.FILE;
+        }
+
+        viewHolder.compositeDisposable.clear();
+
+        if (hasPermanentTimeString(file, position)) {
+            viewHolder.compositeDisposable.add(timestampUpdateTimer.subscribe(t -> {
+                String timeSeparationString = timestampToDetailString(viewHolder.itemView.getContext(), file.getTimestamp());
+                viewHolder.mMsgDetailTxtPerm.setText(timeSeparationString);
+            }));
+            viewHolder.mMsgDetailTxtPerm.setVisibility(View.VISIBLE);
         } else {
-            type = MessageType.FILE_TRANSFER;
+            viewHolder.mMsgDetailTxtPerm.setVisibility(View.GONE);
         }
-        View longPressView = type == MessageType.IMAGE ? viewHolder.mPhoto : (type == MessageType.VIDEO) ? viewHolder.video : (type == MessageType.AUDIO) ? viewHolder.mAudioInfoLayout : viewHolder.mFileInfoLayout;
-        if (type == MessageType.AUDIO || type == MessageType.FILE_TRANSFER) {
+
+        if (interaction.isIncoming()) {
+            viewHolder.mAvatar.setImageBitmap(null);
+            viewHolder.mAvatar.setVisibility(View.VISIBLE);
+            CallContact contact = interaction.getContact();
+            if (contact != null) {
+                viewHolder.mAvatar.setImageDrawable(
+                        conversationFragment.getConversationAvatar(contact.getPrimaryNumber())
+                );
+            }
+        } else {
+            switch (interaction.getStatus()) {
+                case SENDING:
+                    viewHolder.mStatusIcon.setVisibility(View.VISIBLE);
+                    viewHolder.mStatusIcon.setImageResource(R.drawable.baseline_circle_24);
+                    break;
+                case FAILURE:
+                    viewHolder.mStatusIcon.setVisibility(View.VISIBLE);
+                    viewHolder.mStatusIcon.setImageResource(R.drawable.round_highlight_off_24);
+                    break;
+                default:
+                    viewHolder.mStatusIcon.setVisibility(View.VISIBLE);
+                    viewHolder.mStatusIcon.setImageResource(R.drawable.baseline_check_circle_24);
+                    lastDeliveredPosition = position;
+            }
+        }
+
+        View longPressView = type == TransferMsgType.IMAGE ?
+                viewHolder.mImage : (type == TransferMsgType.VIDEO) ?
+                viewHolder.video : (type == TransferMsgType.AUDIO) ?
+                viewHolder.mAudioInfoLayout : viewHolder.mFileInfoLayout;
+        if (type == TransferMsgType.AUDIO || type == TransferMsgType.FILE) {
             longPressView.getBackground().setTintList(null);
         }
 
@@ -420,7 +462,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
             }
         });
         longPressView.setOnLongClickListener(v -> {
-            if (type == MessageType.AUDIO || type == MessageType.FILE_TRANSFER) {
+            if (type == TransferMsgType.AUDIO || type == TransferMsgType.FILE) {
                 conversationFragment.updatePosition(viewHolder.getAdapterPosition());
                 longPressView.getBackground().setTint(conversationFragment.getResources().getColor(R.color.grey_500));
             }
@@ -429,37 +471,24 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
 
         });
 
-        if (type == MessageType.IMAGE) {
-            Context context = viewHolder.mPhoto.getContext();
-
-            LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) viewHolder.mAnswerLayout.getLayoutParams();
-            params.gravity = (file.isOutgoing() ? Gravity.END : Gravity.START) | Gravity.BOTTOM;
-            viewHolder.mAnswerLayout.setLayoutParams(params);
-
-            LinearLayout.LayoutParams imageParams = (LinearLayout.LayoutParams) viewHolder.mPhoto.getLayoutParams();
-            imageParams.height = mPictureMaxSize;
-            viewHolder.mPhoto.setLayoutParams(imageParams);
+        if (type == TransferMsgType.IMAGE) {
+            Context context = viewHolder.mImage.getContext();
 
             GlideApp.with(context)
                     .load(path)
                     .apply(PICTURE_OPTIONS)
-                    .into(new DrawableImageViewTarget(viewHolder.mPhoto).waitForLayout());
+                    .into(new DrawableImageViewTarget(viewHolder.mImage).waitForLayout());
 
-            ((LinearLayout) viewHolder.mAnswerLayout).setGravity(file.isOutgoing() ? Gravity.END : Gravity.START);
-            viewHolder.mPhoto.setOnClickListener(v -> {
+            viewHolder.mImage.setOnClickListener(v -> {
                 Uri contentUri = getUriForFile(v.getContext(), ContentUriHandler.AUTHORITY_FILES, path);
                 Intent i = new Intent(context, MediaViewerActivity.class);
                 i.setAction(Intent.ACTION_VIEW).setDataAndType(contentUri, "image/*").setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                 ActivityOptionsCompat options = ActivityOptionsCompat.
-                        makeSceneTransitionAnimation(conversationFragment.getActivity(), viewHolder.mPhoto, "picture");
+                        makeSceneTransitionAnimation(conversationFragment.getActivity(), viewHolder.mImage, "picture");
                 conversationFragment.startActivityForResult(i, 3006, options.toBundle());
             });
             return;
-        } else if (type == MessageType.VIDEO) {
-            LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) viewHolder.mAnswerLayout.getLayoutParams();
-            params.gravity = file.isOutgoing() ? Gravity.END : Gravity.START;
-            viewHolder.mAnswerLayout.setLayoutParams(params);
-
+        } else if (type == TransferMsgType.VIDEO) {
             Context context = viewHolder.itemView.getContext();
             if (viewHolder.player != null) {
                 viewHolder.player.release();
@@ -526,7 +555,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
             });
             player.seekTo(1);
             return;
-        } else if (type == MessageType.AUDIO) {
+        } else if (type == TransferMsgType.AUDIO) {
             Context context = viewHolder.itemView.getContext();
             try {
                 ((ImageView) viewHolder.btnAccept).setImageResource(R.drawable.baseline_play_arrow_24);
@@ -552,12 +581,16 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
                         player.seekTo(0);
                         ((ImageView) viewHolder.btnAccept).setImageResource(R.drawable.baseline_play_arrow_24);
                     });
-                    viewHolder.updater = new UiUpdater(() -> {
-                        int pS = player.getCurrentPosition() / 1000;
-                        int dS = player.getDuration() / 1000;
-                        viewHolder.mMsgTxt.setText(String.format("%02d:%02d / %02d:%02d", pS / 60, pS % 60, dS / 60, dS % 60));
-                    });
-                    viewHolder.updater.start();
+                    viewHolder.compositeDisposable.add(Observable.interval(1L, TimeUnit.SECONDS)
+                            .subscribeOn(Schedulers.io())
+                            .observeOn(AndroidSchedulers.mainThread())
+                            .startWith(0L)
+                            .subscribe(t -> {
+                                int pS = player.getCurrentPosition() / 1000;
+                                int dS = player.getDuration() / 1000;
+                                viewHolder.mMsgTxt.setText(String.format(Locale.getDefault(),
+                                        "%02d:%02d / %02d:%02d", pS / 60, pS % 60, dS / 60, dS % 60));
+                            }));
                 } else {
                     viewHolder.btnAccept.setOnClickListener(null);
                     viewHolder.btnRefuse.setOnClickListener(null);
@@ -570,15 +603,13 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
         }
 
         if (file.getStatus().isError()) {
-            viewHolder.icon.setImageResource(R.drawable.baseline_warning_24);
+            viewHolder.mIcon.setImageResource(R.drawable.baseline_warning_24);
         } else {
-            viewHolder.icon.setImageResource(R.drawable.baseline_attach_file_24);
+            viewHolder.mIcon.setImageResource(R.drawable.baseline_attach_file_24);
         }
 
         viewHolder.mMsgTxt.setText(file.getDisplayName());
 
-        ((LinearLayout) viewHolder.mLayout).setGravity(file.isOutgoing() ? Gravity.END : Gravity.START);
-
         if (file.getStatus() == InteractionStatus.TRANSFER_AWAITING_HOST) {
             viewHolder.mAnswerLayout.setVisibility(View.VISIBLE);
             viewHolder.btnAccept.setOnClickListener(v -> {
@@ -680,58 +711,56 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
         }
 
         convViewHolder.mMsgTxt.setText(message);
-        if (convViewHolder.mPhoto != null) {
-            convViewHolder.mPhoto.setImageBitmap(null);
-        }
 
-        if (msgSequenceType == SequenceType.LAST || msgSequenceType == SequenceType.SINGLE) {
-            setBottomMargin(convViewHolder.mMsgTxt, 8);
-            if (textMessage.isIncoming()) {
-                convViewHolder.mPhoto.setImageDrawable(
-                        conversationFragment.getConversationAvatar(contact.getPrimaryNumber())
-                );
-            }
+        if (textMessage.isIncoming()) {
+            convViewHolder.mAvatar.setImageBitmap(null);
+            convViewHolder.mAvatar.setVisibility(View.VISIBLE);
         } else {
-            setBottomMargin(convViewHolder.mMsgTxt, 0);
-        }
-
-        if (!textMessage.isIncoming()) {
             switch (textMessage.getStatus()) {
                 case SENDING:
-                    convViewHolder.mPhoto.setVisibility(View.VISIBLE);
-                    convViewHolder.mPhoto.setImageResource(R.drawable.baseline_circle_24);
+                    convViewHolder.mStatusIcon.setVisibility(View.VISIBLE);
+                    convViewHolder.mStatusIcon.setImageResource(R.drawable.baseline_circle_24);
                     break;
                 case FAILURE:
-                    convViewHolder.mPhoto.setVisibility(View.VISIBLE);
-                    convViewHolder.mPhoto.setImageResource(R.drawable.round_highlight_off_24);
+                    convViewHolder.mStatusIcon.setVisibility(View.VISIBLE);
+                    convViewHolder.mStatusIcon.setImageResource(R.drawable.round_highlight_off_24);
                     break;
                 default:
-                    if (shouldSeparateByDetails(textMessage, position) && position == lastOutgoingIndex()) {
-                        convViewHolder.mPhoto.setVisibility(View.VISIBLE);
-                        convViewHolder.mPhoto.setImageResource(R.drawable.baseline_check_circle_24);
+                    if (position == lastOutgoingIndex()) {
+                        convViewHolder.mStatusIcon.setVisibility(View.VISIBLE);
+                        convViewHolder.mStatusIcon.setImageResource(R.drawable.baseline_check_circle_24);
                         lastDeliveredPosition = position;
                     } else {
-                        convViewHolder.mPhoto.setVisibility(View.GONE);
+                        convViewHolder.mStatusIcon.setVisibility(View.GONE);
                     }
             }
         }
 
+        if (msgSequenceType == SequenceType.LAST || msgSequenceType == SequenceType.SINGLE) {
+            setBottomMargin(convViewHolder.mMsgTxt, 8);
+            if (textMessage.isIncoming()) {
+                convViewHolder.mAvatar.setImageDrawable(
+                        conversationFragment.getConversationAvatar(contact.getPrimaryNumber())
+                );
+            }
+        } else {
+            setBottomMargin(convViewHolder.mMsgTxt, 0);
+        }
+
         if (isTimeShown) {
-            convViewHolder.updater = new UiUpdater(() -> {
+            convViewHolder.compositeDisposable.add(timestampUpdateTimer.subscribe(t -> {
                 String timeSeparationString = timestampToDetailString(context, textMessage.getTimestamp());
                 convViewHolder.mMsgDetailTxtPerm.setText(timeSeparationString);
-            }, TIMESTAMP_UPDATE_INTERVAL);
-            convViewHolder.updater.start();
+            }));
             convViewHolder.mMsgDetailTxtPerm.setVisibility(View.VISIBLE);
         } else {
             convViewHolder.mMsgDetailTxtPerm.setVisibility(View.GONE);
             final boolean isExpanded = position == expandedItemPosition;
             if (isExpanded) {
-                convViewHolder.updater = new UiUpdater(() -> {
+                convViewHolder.compositeDisposable.add(timestampUpdateTimer.subscribe(t -> {
                     String timeSeparationString = timestampToDetailString(context, textMessage.getTimestamp());
                     convViewHolder.mMsgDetailTxt.setText(timeSeparationString);
-                }, TIMESTAMP_UPDATE_INTERVAL);
-                convViewHolder.updater.start();
+                }));
             }
             setItemViewExpansionState(convViewHolder, isExpanded);
             convViewHolder.mMsgTxt.setOnClickListener((View v) -> {
@@ -755,11 +784,10 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
         } else if (event.event == ContactEvent.Event.INCOMING_REQUEST) {
             viewHolder.mMsgTxt.setText(R.string.hist_invitation_received);
         }
-        viewHolder.updater = new UiUpdater(() -> {
+        viewHolder.compositeDisposable.add(timestampUpdateTimer.subscribe(t -> {
             String timeSeparationString = timestampToDetailString(viewHolder.itemView.getContext(), event.getTimestamp());
             viewHolder.mMsgDetailTxt.setText(timeSeparationString);
-        }, TIMESTAMP_UPDATE_INTERVAL);
-        viewHolder.updater.start();
+        }));
     }
 
     /**
@@ -772,7 +800,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
                                                  @NonNull final Interaction interaction) {
         int pictureResID;
         String historyTxt;
-        convViewHolder.mPhoto.setScaleY(1);
+        convViewHolder.mIcon.setScaleY(1);
         Context context = convViewHolder.itemView.getContext();
 
 
@@ -807,7 +835,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
             } else {
                 pictureResID = R.drawable.baseline_call_missed_outgoing_24;
                 // Flip the photo upside down to show a "missed outgoing call"
-                convViewHolder.mPhoto.setScaleY(-1);
+                convViewHolder.mIcon.setScaleY(-1);
             }
             historyTxt = call.isIncoming() ?
                     context.getString(R.string.notif_missed_incoming_call) :
@@ -822,7 +850,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
         }
 
         convViewHolder.mCid = call.getConversation().getParticipant();
-        convViewHolder.mPhoto.setImageResource(pictureResID);
+        convViewHolder.mIcon.setImageResource(pictureResID);
         convViewHolder.mHistTxt.setText(historyTxt);
         convViewHolder.mHistDetailTxt.setText(DateFormat.getDateTimeInstance()
                 .format(call.getTimestamp())); // start date
@@ -887,45 +915,15 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
         return null;
     }
 
-    /**
-     * Helper used to determine if a text details string should be displayed for a message at a
-     * certain position.
-     *
-     * @param msg       The Interaction at the given position
-     * @param position The position of the current message
-     * @return true if a text details string should be displayed under the message
-     */
-    private boolean shouldSeparateByDetails(final Interaction msg, int position) {
-        if (msg == null) {
-            return false;
-        }
-        boolean shouldSeparateMsg = false;
-        Interaction previousTextMessage = getPreviousMessageFromPosition(position);
-        if (previousTextMessage != null) {
-            shouldSeparateMsg = true;
-            Interaction nextTextMessage = getNextMessageFromPosition(position);
-            if (nextTextMessage != null) {
-                long diff = nextTextMessage.getTimestamp() - msg.getTimestamp();
-                if (diff < DateUtils.MINUTE_IN_MILLIS) {
-                    shouldSeparateMsg = false;
-                }
-            }
-        }
-        return shouldSeparateMsg;
-    }
-
     private boolean isSeqBreak(@NonNull Interaction first, @NonNull Interaction second) {
         return StringUtils.isOnlyEmoji(first.getBody()) != StringUtils.isOnlyEmoji(second.getBody())
                 || first.isIncoming() != second.isIncoming()
-                || first.getType() == InteractionType.CONTACT
-                || first.getType() == InteractionType.CALL
-                || second.getType() == InteractionType.CONTACT
-                || second.getType() == InteractionType.CALL;
+                || first.getType() != InteractionType.TEXT
+                || second.getType() != InteractionType.TEXT;
     }
 
     private boolean isAlwaysSingleMsg(@NonNull Interaction msg) {
-        return msg.getType() == InteractionType.CONTACT
-                || msg.getType() == InteractionType.CALL
+        return msg.getType() != InteractionType.TEXT
                 || StringUtils.isOnlyEmoji(msg.getBody());
     }
 
@@ -1042,14 +1040,18 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
     }
 
     public enum MessageType {
-        INCOMING_TEXT_MESSAGE(R.layout.item_conv_msg_peer),
-        OUTGOING_TEXT_MESSAGE(R.layout.item_conv_msg_me),
+        INCOMING_FILE(R.layout.item_conv_file_peer),
+        INCOMING_IMAGE(R.layout.item_conv_image_peer),
+        INCOMING_AUDIO(R.layout.item_conv_audio_peer),
+        INCOMING_VIDEO(R.layout.item_conv_video_peer),
+        OUTGOING_FILE(R.layout.item_conv_file_me),
+        OUTGOING_IMAGE(R.layout.item_conv_image_me),
+        OUTGOING_AUDIO(R.layout.item_conv_audio_me),
+        OUTGOING_VIDEO(R.layout.item_conv_video_me),
+        CONTACT_EVENT(R.layout.item_conv_contact),
         CALL_INFORMATION(R.layout.item_conv_call),
-        FILE_TRANSFER(R.layout.item_conv_file),
-        IMAGE(R.layout.item_conv_image),
-        AUDIO(R.layout.item_conv_audio),
-        VIDEO(R.layout.item_conv_video),
-        CONTACT_EVENT(R.layout.item_conv_contact);
+        INCOMING_TEXT_MESSAGE(R.layout.item_conv_msg_peer),
+        OUTGOING_TEXT_MESSAGE(R.layout.item_conv_msg_me);
 
         private final int layout;
 
@@ -1057,4 +1059,11 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
             layout = l;
         }
     }
-}
\ No newline at end of file
+
+    private enum TransferMsgType {
+        FILE,
+        IMAGE,
+        AUDIO,
+        VIDEO;
+    }
+}
diff --git a/ring-android/app/src/main/java/cx/ring/views/ConversationViewHolder.java b/ring-android/app/src/main/java/cx/ring/views/ConversationViewHolder.java
index 97552de25..79ab35c37 100644
--- a/ring-android/app/src/main/java/cx/ring/views/ConversationViewHolder.java
+++ b/ring-android/app/src/main/java/cx/ring/views/ConversationViewHolder.java
@@ -36,19 +36,23 @@ import android.widget.TextView;
 import cx.ring.R;
 import cx.ring.adapters.ConversationAdapter;
 import cx.ring.utils.UiUpdater;
+import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.disposables.Disposable;
 
 public class ConversationViewHolder extends RecyclerView.ViewHolder {
     public TextView mMsgTxt;
     public TextView mMsgDetailTxt;
     public TextView mMsgDetailTxtPerm;
-    public ImageView mPhoto;
+    public ImageView mAvatar;
+    public ImageView mImage;
+    public ImageView mStatusIcon;
+    public ImageView mIcon;
     public TextView mHistTxt;
     public TextView mHistDetailTxt;
     public View mLayout;
     public ViewGroup mAnswerLayout;
     public View btnAccept;
     public View btnRefuse;
-    public ImageView icon;
     public ProgressBar progress;
     public MediaPlayer player;
     public TextureView video;
@@ -58,52 +62,82 @@ public class ConversationViewHolder extends RecyclerView.ViewHolder {
     public LinearLayout mCallInfoLayout, mFileInfoLayout, mAudioInfoLayout;
     public ValueAnimator animator;
 
+    public CompositeDisposable compositeDisposable = new CompositeDisposable();
+
     public ConversationViewHolder(ViewGroup v, ConversationAdapter.MessageType type) {
         super(v);
-        if (type == ConversationAdapter.MessageType.INCOMING_TEXT_MESSAGE) {
-            mMsgTxt = v.findViewById(R.id.msg_txt);
-            mMsgDetailTxt = v.findViewById(R.id.msg_details_txt);
-            mMsgDetailTxtPerm = v.findViewById(R.id.msg_details_txt_perm);
-            mPhoto = v.findViewById(R.id.photo);
-        } else if (type == ConversationAdapter.MessageType.OUTGOING_TEXT_MESSAGE) {
-            mMsgTxt = v.findViewById(R.id.msg_txt);
-            mMsgDetailTxt = v.findViewById(R.id.msg_details_txt);
-            mMsgDetailTxtPerm = v.findViewById(R.id.msg_details_txt_perm);
-            mPhoto = v.findViewById(R.id.status_icon);
+        if (type == ConversationAdapter.MessageType.CONTACT_EVENT) {
+            mMsgTxt = v.findViewById(R.id.contact_event_txt);
+            mMsgDetailTxt = v.findViewById(R.id.contact_event_details_txt);
         } else if (type == ConversationAdapter.MessageType.CALL_INFORMATION) {
             mHistTxt = v.findViewById(R.id.call_hist_txt);
             mHistDetailTxt = v.findViewById(R.id.call_details_txt);
-            mPhoto = v.findViewById(R.id.call_icon);
+            mIcon = v.findViewById(R.id.call_icon);
             mCallInfoLayout = v.findViewById(R.id.callInfoLayout);
-
-        } else if (type == ConversationAdapter.MessageType.FILE_TRANSFER) {
-            mMsgTxt = v.findViewById(R.id.call_hist_filename);
-            mMsgDetailTxt = v.findViewById(R.id.file_details_txt);
-            mLayout = v.findViewById(R.id.file_layout);
-            mFileInfoLayout = v.findViewById(R.id.fileInfoLayout);
-            mAnswerLayout = v.findViewById(R.id.llAnswer);
-            btnAccept = v.findViewById(R.id.btnAccept);
-            btnRefuse = v.findViewById(R.id.btnRefuse);
-            progress = v.findViewById(R.id.progress);
-            icon = v.findViewById(R.id.file_icon);
-        } else if (type == ConversationAdapter.MessageType.IMAGE) {
-            mPhoto = v.findViewById(R.id.image);
-            mAnswerLayout = v.findViewById(R.id.imageLayout);
-            mMsgDetailTxt = v.findViewById(R.id.msg_details_txt);
-        } else if (type == ConversationAdapter.MessageType.VIDEO) {
-            mLayout = v.findViewById(R.id.video_frame);
-            video = v.findViewById(R.id.video);
-            mAnswerLayout = v.findViewById(R.id.imageLayout);
-            mMsgDetailTxt = v.findViewById(R.id.msg_details_txt);
-        } else if (type == ConversationAdapter.MessageType.AUDIO) {
-            btnAccept = v.findViewById(R.id.play);
-            btnRefuse = v.findViewById(R.id.replay);
-            mMsgTxt = v.findViewById(R.id.msg_txt);
-            mAudioInfoLayout = v.findViewById(R.id.audioInfoLayout);
-            mMsgDetailTxt = v.findViewById(R.id.file_details_txt);
-        } else if (type == ConversationAdapter.MessageType.CONTACT_EVENT) {
-            mMsgTxt = v.findViewById(R.id.contact_event_txt);
-            mMsgDetailTxt = v.findViewById(R.id.contact_event_details_txt);
+        } else {
+            switch (type) {
+                // common layout elements
+                case INCOMING_TEXT_MESSAGE:
+                case OUTGOING_TEXT_MESSAGE:
+                    mMsgTxt = v.findViewById(R.id.msg_txt);
+                    mMsgDetailTxt = v.findViewById(R.id.msg_details_txt);
+                    mMsgDetailTxtPerm = v.findViewById(R.id.msg_details_txt_perm);
+                    break;
+                case INCOMING_FILE:
+                case OUTGOING_FILE:
+                    mMsgTxt = v.findViewById(R.id.call_hist_filename);
+                    mMsgDetailTxt = v.findViewById(R.id.file_details_txt);
+                    mLayout = v.findViewById(R.id.file_layout);
+                    mFileInfoLayout = v.findViewById(R.id.fileInfoLayout);
+                    mIcon = v.findViewById(R.id.file_icon);
+                    progress = v.findViewById(R.id.progress);
+                    mAnswerLayout = v.findViewById(R.id.llAnswer);
+                    btnAccept = v.findViewById(R.id.btnAccept);
+                    btnRefuse = v.findViewById(R.id.btnRefuse);
+                    mMsgDetailTxtPerm = v.findViewById(R.id.msg_details_txt_perm);
+                    break;
+                case INCOMING_IMAGE:
+                case OUTGOING_IMAGE:
+                    mImage = v.findViewById(R.id.image);
+                    mAnswerLayout = v.findViewById(R.id.imageLayout);
+                    mMsgDetailTxtPerm = v.findViewById(R.id.msg_details_txt_perm);
+                    mMsgDetailTxt = v.findViewById(R.id.msg_details_txt);
+                    break;
+                case INCOMING_AUDIO:
+                case OUTGOING_AUDIO:
+                    btnAccept = v.findViewById(R.id.play);
+                    btnRefuse = v.findViewById(R.id.replay);
+                    mMsgTxt = v.findViewById(R.id.msg_txt);
+                    mAudioInfoLayout = v.findViewById(R.id.audioInfoLayout);
+                    mMsgDetailTxt = v.findViewById(R.id.file_details_txt);
+                    mMsgDetailTxtPerm = v.findViewById(R.id.msg_details_txt_perm);
+                    break;
+                case INCOMING_VIDEO:
+                case OUTGOING_VIDEO:
+                    mLayout = v.findViewById(R.id.video_frame);
+                    video = v.findViewById(R.id.video);
+                    mAnswerLayout = v.findViewById(R.id.imageLayout);
+                    mMsgDetailTxt = v.findViewById(R.id.msg_details_txt);
+                    mMsgDetailTxtPerm = v.findViewById(R.id.msg_details_txt_perm);
+                    break;
+            }
+            // msg-direction-specific layout elements
+            switch (type) {
+                case INCOMING_TEXT_MESSAGE:
+                case INCOMING_FILE:
+                case INCOMING_IMAGE:
+                case INCOMING_AUDIO:
+                case INCOMING_VIDEO:
+                    mAvatar = v.findViewById(R.id.photo);
+                    break;
+                case OUTGOING_TEXT_MESSAGE:
+                case OUTGOING_FILE:
+                case OUTGOING_IMAGE:
+                case OUTGOING_AUDIO:
+                case OUTGOING_VIDEO:
+                    mStatusIcon = v.findViewById(R.id.status_icon);
+                    break;
+            }
         }
     }
 }
diff --git a/ring-android/app/src/main/res/layout/item_conv_audio.xml b/ring-android/app/src/main/res/layout/item_conv_audio.xml
deleted file mode 100644
index 77d0fb7d6..000000000
--- a/ring-android/app/src/main/res/layout/item_conv_audio.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2004-2019 Savoir-faire Linux Inc.
-  ~
-  ~ Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
-  ~
-  ~ 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, write to the Free Software
-  ~  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-  -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/file_layout"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginTop="8dp"
-    android:layout_marginBottom="8dp"
-    android:clickable="true"
-    android:descendantFocusability="blocksDescendants"
-    android:focusable="true"
-    android:orientation="vertical"
-    android:paddingStart="16dp"
-    android:paddingEnd="16dp">
-
-    <LinearLayout
-        android:id="@+id/audioInfoLayout"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="56dp"
-        android:background="@drawable/textmsg_call_background"
-        android:gravity="center_vertical"
-        android:orientation="horizontal"
-        android:padding="4dp">
-
-        <ImageView
-            android:id="@+id/play"
-            android:layout_width="32dp"
-            android:layout_height="32dp"
-            android:layout_gravity="center_vertical"
-            android:layout_marginStart="4dp"
-            android:layout_marginEnd="4dp"
-            android:background="?selectableItemBackgroundBorderless"
-            android:contentDescription="@string/lb_playback_controls_play"
-            app:srcCompat="@drawable/baseline_play_arrow_24" />
-
-        <ImageView
-            android:id="@+id/replay"
-            android:layout_width="28dp"
-            android:layout_height="28dp"
-            android:layout_gravity="center_vertical"
-            android:layout_marginStart="4dp"
-            android:layout_marginEnd="4dp"
-            android:background="?selectableItemBackgroundBorderless"
-            android:contentDescription="@string/lb_playback_controls_rewind"
-            app:srcCompat="@drawable/baseline_replay_24" />
-
-        <TextView
-            android:id="@+id/msg_txt"
-            android:layout_width="120dp"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="8dp"
-            android:layout_marginEnd="16dp"
-            android:ellipsize="middle"
-            android:gravity="center_horizontal"
-            android:scrollHorizontally="true"
-            android:singleLine="true"
-            android:textAppearance="@android:style/TextAppearance.Medium"
-            android:textColor="@color/textColorSecondary"
-            tools:text="2:03 / 2:45" />
-
-    </LinearLayout>
-
-    <TextView
-        android:id="@+id/file_details_txt"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="72dp"
-        android:layout_marginEnd="8dp"
-        android:maxWidth="200dp"
-        android:textColor="@color/textColorSecondary"
-        android:textSize="12sp"
-        tools:text="1 mo - 12 mars" />
-
-</LinearLayout>
diff --git a/ring-android/app/src/main/res/layout/item_conv_audio_me.xml b/ring-android/app/src/main/res/layout/item_conv_audio_me.xml
new file mode 100644
index 000000000..b70ffcc26
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/item_conv_audio_me.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2004-2019 Savoir-faire Linux Inc.
+  ~
+  ~ Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+  ~
+  ~ 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, write to the Free Software
+  ~  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/file_layout"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="8dp"
+    android:background="@android:color/transparent"
+    android:clickable="true"
+    android:descendantFocusability="blocksDescendants"
+    android:focusable="true"
+    android:orientation="vertical"
+    android:gravity="center">
+
+    <TextView
+        android:id="@+id/msg_details_txt_perm"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:textColor="@color/textColorSecondary"
+        android:textSize="14sp"
+        android:paddingTop="4dp"
+        android:paddingBottom="8dp"
+        tools:text="@string/time_just_now" />
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="8dp"
+        android:paddingEnd="2dp">
+
+        <LinearLayout
+            android:id="@+id/audioInfoLayout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentEnd="true"
+            android:layout_marginStart="72dp"
+            android:layout_marginEnd="24dp"
+            android:background="@drawable/textmsg_call_background"
+            android:gravity="center_vertical"
+            android:orientation="horizontal"
+            android:padding="4dp">
+
+            <ImageView
+                android:id="@+id/play"
+                android:layout_width="32dp"
+                android:layout_height="32dp"
+                android:layout_gravity="center_vertical"
+                android:layout_marginStart="4dp"
+                android:layout_marginEnd="4dp"
+                android:background="?selectableItemBackgroundBorderless"
+                android:contentDescription="@string/lb_playback_controls_play"
+                app:srcCompat="@drawable/baseline_play_arrow_24" />
+
+            <ImageView
+                android:id="@+id/replay"
+                android:layout_width="28dp"
+                android:layout_height="28dp"
+                android:layout_gravity="center_vertical"
+                android:layout_marginStart="4dp"
+                android:layout_marginEnd="4dp"
+                android:background="?selectableItemBackgroundBorderless"
+                android:contentDescription="@string/lb_playback_controls_rewind"
+                app:srcCompat="@drawable/baseline_replay_24" />
+
+            <TextView
+                android:id="@+id/msg_txt"
+                android:layout_width="120dp"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="8dp"
+                android:layout_marginEnd="16dp"
+                android:ellipsize="middle"
+                android:gravity="center_horizontal"
+                android:scrollHorizontally="true"
+                android:singleLine="true"
+                android:textAppearance="@android:style/TextAppearance.Medium"
+                android:textColor="@color/textColorSecondary"
+                tools:text="2:03 / 2:45" />
+
+        </LinearLayout>
+
+        <ImageView
+            android:id="@+id/status_icon"
+            android:layout_width="14dp"
+            android:layout_height="14dp"
+            android:layout_alignBottom="@id/audioInfoLayout"
+            android:layout_alignParentEnd="true"
+            android:layout_marginEnd="5dp"
+            android:tint="@color/grey_500"
+            app:srcCompat="@drawable/round_check_circle_24" />
+
+        <TextView
+            android:id="@+id/file_details_txt"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignEnd="@id/audioInfoLayout"
+            android:layout_below="@id/audioInfoLayout"
+            android:layout_marginEnd="@dimen/conversation_message_radius"
+            android:maxWidth="200dp"
+            android:textColor="@color/textColorSecondary"
+            android:textSize="12sp"
+            tools:text="1 mo - 12 mars" />
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/ring-android/app/src/main/res/layout/item_conv_audio_peer.xml b/ring-android/app/src/main/res/layout/item_conv_audio_peer.xml
new file mode 100644
index 000000000..31df51e2b
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/item_conv_audio_peer.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2004-2019 Savoir-faire Linux Inc.
+  ~
+  ~ Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+  ~
+  ~ 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, write to the Free Software
+  ~  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/file_layout"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="8dp"
+    android:clickable="true"
+    android:descendantFocusability="blocksDescendants"
+    android:focusable="true"
+    android:orientation="vertical"
+    android:gravity="center">
+
+    <TextView
+        android:id="@+id/msg_details_txt_perm"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:textColor="@color/textColorSecondary"
+        android:textSize="14sp"
+        android:paddingTop="4dp"
+        tools:text="@string/time_just_now" />
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="8dp"
+        android:paddingStart="@dimen/padding_medium"
+        android:paddingEnd="@dimen/padding_large">
+
+        <ImageView
+            android:id="@+id/photo"
+            android:layout_width="@dimen/conversation_avatar_size"
+            android:layout_height="@dimen/conversation_avatar_size"
+            android:background="@null"
+            android:layout_alignBottom="@id/audioInfoLayout"
+            android:scaleType="centerCrop"/>
+
+        <LinearLayout
+            android:id="@+id/audioInfoLayout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toEndOf="@+id/photo"
+            android:layout_marginStart="@dimen/padding_medium"
+            android:layout_marginEnd="20dp"
+            android:background="@drawable/textmsg_call_background"
+            android:gravity="center_vertical"
+            android:orientation="horizontal"
+            android:padding="4dp">
+
+            <ImageView
+                android:id="@+id/play"
+                android:layout_width="32dp"
+                android:layout_height="32dp"
+                android:layout_gravity="center_vertical"
+                android:layout_marginStart="4dp"
+                android:layout_marginEnd="4dp"
+                android:background="?selectableItemBackgroundBorderless"
+                android:contentDescription="@string/lb_playback_controls_play"
+                app:srcCompat="@drawable/baseline_play_arrow_24" />
+
+            <ImageView
+                android:id="@+id/replay"
+                android:layout_width="28dp"
+                android:layout_height="31dp"
+                android:layout_gravity="center_vertical"
+                android:layout_marginStart="4dp"
+                android:layout_marginEnd="4dp"
+                android:background="?selectableItemBackgroundBorderless"
+                android:contentDescription="@string/lb_playback_controls_rewind"
+                app:srcCompat="@drawable/baseline_replay_24" />
+
+            <TextView
+                android:id="@+id/msg_txt"
+                android:layout_width="120dp"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="8dp"
+                android:layout_marginEnd="16dp"
+                android:ellipsize="middle"
+                android:gravity="center_horizontal"
+                android:scrollHorizontally="true"
+                android:singleLine="true"
+                android:textAppearance="@android:style/TextAppearance.Medium"
+                android:textColor="@color/textColorSecondary"
+                tools:text="2:03 / 2:45" />
+
+        </LinearLayout>
+
+        <TextView
+            android:id="@+id/file_details_txt"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignStart="@+id/audioInfoLayout"
+            android:layout_below="@+id/audioInfoLayout"
+            android:layout_marginStart="@dimen/conversation_message_radius"
+            android:textColor="@color/textColorSecondary"
+            android:textSize="12sp"
+            tools:text="1 mo - 12 mars" />
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/ring-android/app/src/main/res/layout/item_conv_file.xml b/ring-android/app/src/main/res/layout/item_conv_file.xml
deleted file mode 100644
index d009f86a8..000000000
--- a/ring-android/app/src/main/res/layout/item_conv_file.xml
+++ /dev/null
@@ -1,131 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2004-2019 Savoir-faire Linux Inc.
-  ~
-  ~ Author: Pierre Duchemin <pierre.duchemin@savoirfairelinux.com>
-  ~
-  ~ 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, write to the Free Software
-  ~  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-  -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/file_layout"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginBottom="8dp"
-    android:layout_marginTop="8dp"
-    android:clickable="true"
-    android:descendantFocusability="blocksDescendants"
-    android:focusable="true"
-    android:orientation="vertical"
-    android:paddingEnd="16dp"
-    android:paddingStart="16dp">
-
-    <LinearLayout
-        android:id="@+id/fileInfoLayout"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="56dp"
-        android:background="@drawable/textmsg_call_background"
-        android:orientation="horizontal">
-
-        <ImageView
-            android:id="@+id/file_icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical"
-            android:layout_marginStart="12dp"
-            android:layout_marginEnd="4dp"
-            app:srcCompat="@drawable/baseline_attach_file_24" />
-
-        <LinearLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:animateLayoutChanges="true"
-            android:minWidth="160dp"
-            android:orientation="vertical"
-            android:paddingTop="8dp">
-
-            <TextView
-                android:id="@+id/call_hist_filename"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginStart="8dp"
-                android:layout_marginEnd="16dp"
-                android:layout_marginBottom="4dp"
-                android:ellipsize="middle"
-                android:maxWidth="200dp"
-                android:scrollHorizontally="true"
-                android:singleLine="true"
-                android:textAppearance="@android:style/TextAppearance.Medium"
-                android:textColor="@color/textColorPrimary"
-                tools:text="long_file_name_gtest.jpg" />
-
-            <TextView
-                android:id="@+id/file_details_txt"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginStart="8dp"
-                android:layout_marginEnd="24dp"
-                android:layout_marginBottom="8dp"
-                android:maxWidth="200dp"
-                android:textColor="@color/textColorSecondary"
-                android:textSize="12sp"
-                tools:text="1 mo - 12 mars" />
-
-            <ProgressBar
-                android:id="@+id/progress"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:indeterminate="false"
-                android:paddingBottom="8dp"
-                android:visibility="gone"
-                tools:visibility="gone" />
-
-            <LinearLayout
-                android:id="@+id/llAnswer"
-                android:layout_width="match_parent"
-                android:layout_height="42dp"
-                android:minWidth="350dp"
-                android:orientation="horizontal"
-                android:visibility="gone"
-                tools:visibility="visible">
-
-                <com.google.android.material.button.MaterialButton
-                    android:id="@+id/btnRefuse"
-                    style="@style/Widget.MaterialComponents.Button.TextButton"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="0.5"
-                    android:background="?attr/selectableItemBackground"
-                    android:gravity="center"
-                    android:text="@string/refuse"
-                    android:textColor="@color/color_primary_dark" />
-
-                <com.google.android.material.button.MaterialButton
-                    android:id="@+id/btnAccept"
-                    style="@style/Widget.MaterialComponents.Button.TextButton"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="0.5"
-                    android:background="?attr/selectableItemBackground"
-                    android:gravity="center"
-                    android:text="@string/accept"
-                    android:textColor="@color/color_primary_dark" />
-            </LinearLayout>
-
-        </LinearLayout>
-    </LinearLayout>
-
-</LinearLayout>
diff --git a/ring-android/app/src/main/res/layout/item_conv_file_me.xml b/ring-android/app/src/main/res/layout/item_conv_file_me.xml
new file mode 100644
index 000000000..e58494058
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/item_conv_file_me.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2004-2019 Savoir-faire Linux Inc.
+  ~
+  ~ Author: Pierre Duchemin <pierre.duchemin@savoirfairelinux.com>
+  ~
+  ~ 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, write to the Free Software
+  ~  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/file_layout"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="8dp"
+    android:background="@android:color/transparent"
+    android:clickable="true"
+    android:descendantFocusability="blocksDescendants"
+    android:focusable="true"
+    android:orientation="vertical"
+    android:gravity="center">
+
+    <TextView
+        android:id="@+id/msg_details_txt_perm"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:textColor="@color/textColorSecondary"
+        android:textSize="14sp"
+        android:paddingTop="4dp"
+        tools:text="@string/time_just_now" />
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="8dp"
+        android:paddingEnd="2dp">
+
+        <LinearLayout
+            android:id="@+id/fileInfoLayout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentEnd="true"
+            android:layout_marginStart="72dp"
+            android:layout_marginEnd="24dp"
+            android:background="@drawable/textmsg_call_background"
+            android:orientation="horizontal">
+
+            <ImageView
+                android:id="@+id/file_icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_marginStart="12dp"
+                android:layout_marginEnd="4dp"
+                app:srcCompat="@drawable/baseline_attach_file_24" />
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:animateLayoutChanges="true"
+                android:minWidth="160dp"
+                android:orientation="vertical"
+                android:paddingTop="8dp">
+
+                <TextView
+                    android:id="@+id/call_hist_filename"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="8dp"
+                    android:layout_marginEnd="16dp"
+                    android:layout_marginBottom="4dp"
+                    android:ellipsize="middle"
+                    android:maxWidth="200dp"
+                    android:scrollHorizontally="true"
+                    android:singleLine="true"
+                    android:textAppearance="@android:style/TextAppearance.Medium"
+                    android:textColor="@color/textColorPrimary"
+                    android:textSize="16sp"
+                    tools:text="long_file_name_gtest_long_file_name_gtest.jpg" />
+
+                <TextView
+                    android:id="@+id/file_details_txt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="8dp"
+                    android:layout_marginEnd="24dp"
+                    android:layout_marginBottom="8dp"
+                    android:maxWidth="200dp"
+                    android:textColor="@color/textColorSecondary"
+                    android:textSize="12sp"
+                    tools:text="1 mo - 12 mars" />
+
+                <ProgressBar
+                    android:id="@+id/progress"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:indeterminate="false"
+                    android:paddingBottom="8dp"
+                    android:visibility="gone"
+                    tools:visibility="gone" />
+
+                <LinearLayout
+                    android:id="@+id/llAnswer"
+                    android:layout_width="match_parent"
+                    android:layout_height="42dp"
+                    android:minWidth="350dp"
+                    android:orientation="horizontal"
+                    android:visibility="gone"
+                    tools:visibility="visible">
+
+                    <com.google.android.material.button.MaterialButton
+                        android:id="@+id/btnRefuse"
+                        style="@style/Widget.MaterialComponents.Button.TextButton"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="0.5"
+                        android:background="?attr/selectableItemBackground"
+                        android:gravity="center"
+                        android:text="@string/refuse"
+                        android:textColor="@color/color_primary_dark" />
+
+                    <com.google.android.material.button.MaterialButton
+                        android:id="@+id/btnAccept"
+                        style="@style/Widget.MaterialComponents.Button.TextButton"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="0.5"
+                        android:background="?attr/selectableItemBackground"
+                        android:gravity="center"
+                        android:text="@string/accept"
+                        android:textColor="@color/color_primary_dark" />
+
+                </LinearLayout>
+
+            </LinearLayout>
+
+        </LinearLayout>
+
+        <ImageView
+            android:id="@+id/status_icon"
+            android:layout_width="14dp"
+            android:layout_height="14dp"
+            android:layout_alignBottom="@id/fileInfoLayout"
+            android:layout_alignParentEnd="true"
+            android:layout_marginEnd="5dp"
+            android:tint="@color/grey_500"
+            app:srcCompat="@drawable/round_check_circle_24" />
+
+    </RelativeLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/item_conv_file_peer.xml b/ring-android/app/src/main/res/layout/item_conv_file_peer.xml
new file mode 100644
index 000000000..f5913be05
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/item_conv_file_peer.xml
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2004-2019 Savoir-faire Linux Inc.
+  ~
+  ~ Author: Pierre Duchemin <pierre.duchemin@savoirfairelinux.com>
+  ~
+  ~ 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, write to the Free Software
+  ~  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/file_layout"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="8dp"
+    android:clickable="true"
+    android:descendantFocusability="blocksDescendants"
+    android:focusable="true"
+    android:orientation="vertical"
+    android:gravity="center">
+
+    <TextView
+        android:id="@+id/msg_details_txt_perm"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:textColor="@color/textColorSecondary"
+        android:textSize="14sp"
+        android:paddingTop="4dp"
+        tools:text="@string/time_just_now" />
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="8dp"
+        android:paddingStart="@dimen/padding_medium"
+        android:paddingEnd="@dimen/padding_large">
+
+        <ImageView
+            android:id="@+id/photo"
+            android:layout_width="@dimen/conversation_avatar_size"
+            android:layout_height="@dimen/conversation_avatar_size"
+            android:background="@null"
+            android:layout_alignBottom="@id/fileInfoLayout"
+            android:scaleType="centerCrop"/>
+
+        <LinearLayout
+            android:id="@+id/fileInfoLayout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toEndOf="@+id/photo"
+            android:layout_marginStart="@dimen/padding_medium"
+            android:layout_marginEnd="20dp"
+            android:background="@drawable/textmsg_call_background"
+            android:orientation="horizontal">
+
+            <ImageView
+                android:id="@+id/file_icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_marginStart="12dp"
+                android:layout_marginEnd="4dp"
+                app:srcCompat="@drawable/baseline_attach_file_24" />
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:animateLayoutChanges="true"
+                android:minWidth="160dp"
+                android:orientation="vertical"
+                android:paddingTop="8dp">
+
+                <TextView
+                    android:id="@+id/call_hist_filename"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="8dp"
+                    android:layout_marginEnd="16dp"
+                    android:layout_marginBottom="4dp"
+                    android:ellipsize="middle"
+                    android:maxWidth="200dp"
+                    android:scrollHorizontally="true"
+                    android:singleLine="true"
+                    android:textAppearance="@android:style/TextAppearance.Medium"
+                    android:textColor="@color/textColorPrimary"
+                    android:textSize="16sp"
+                    tools:text="long_file_name_gtest.jpg" />
+
+                <TextView
+                    android:id="@+id/file_details_txt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="8dp"
+                    android:layout_marginEnd="24dp"
+                    android:layout_marginBottom="8dp"
+                    android:maxWidth="200dp"
+                    android:textColor="@color/textColorSecondary"
+                    android:textSize="12sp"
+                    tools:text="1 mo - 12 mars" />
+
+                <ProgressBar
+                    android:id="@+id/progress"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:indeterminate="false"
+                    android:paddingBottom="8dp"
+                    android:visibility="gone"
+                    tools:visibility="gone" />
+
+                <LinearLayout
+                    android:id="@+id/llAnswer"
+                    android:layout_width="match_parent"
+                    android:layout_height="42dp"
+                    android:minWidth="350dp"
+                    android:orientation="horizontal"
+                    android:visibility="gone"
+                    tools:visibility="visible">
+
+                    <com.google.android.material.button.MaterialButton
+                        android:id="@+id/btnRefuse"
+                        style="@style/Widget.MaterialComponents.Button.TextButton"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="0.5"
+                        android:background="?attr/selectableItemBackground"
+                        android:gravity="center"
+                        android:text="@string/refuse"
+                        android:textColor="@color/color_primary_dark" />
+
+                    <com.google.android.material.button.MaterialButton
+                        android:id="@+id/btnAccept"
+                        style="@style/Widget.MaterialComponents.Button.TextButton"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="0.5"
+                        android:background="?attr/selectableItemBackground"
+                        android:gravity="center"
+                        android:text="@string/accept"
+                        android:textColor="@color/color_primary_dark" />
+
+                </LinearLayout>
+
+            </LinearLayout>
+
+        </LinearLayout>
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/ring-android/app/src/main/res/layout/item_conv_image.xml b/ring-android/app/src/main/res/layout/item_conv_image.xml
deleted file mode 100644
index 55e5bfb32..000000000
--- a/ring-android/app/src/main/res/layout/item_conv_image.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2004-2019 Savoir-faire Linux Inc.
-  ~
-  ~ Author: Pierre Duchemin <pierre.duchemin@savoirfairelinux.com>
-  ~
-  ~ 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, write to the Free Software
-  ~  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-  -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginBottom="8dp"
-    android:layout_marginTop="8dp"
-    android:clickable="true"
-    android:descendantFocusability="blocksDescendants"
-    android:focusable="true"
-    android:orientation="vertical"
-    android:paddingEnd="@dimen/padding_large"
-    android:paddingLeft="@dimen/padding_large"
-    android:paddingRight="@dimen/padding_large"
-    android:paddingStart="@dimen/padding_large">
-
-    <LinearLayout
-        android:id="@+id/imageLayout"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="end"
-        android:orientation="vertical"
-        android:paddingStart="56dp">
-
-        <cx.ring.views.ConversationImageView
-            android:id="@+id/image"
-            android:layout_width="wrap_content"
-            android:layout_height="200dp"
-            android:adjustViewBounds="true"
-            android:maxHeight="200dp"
-            android:transitionName="picture"
-            tools:src="@drawable/logo_sfl_coul_rgb" />
-
-        <TextView
-            android:id="@+id/msg_details_txt"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginBottom="4dp"
-            android:textColor="@color/textColorSecondary"
-            android:textSize="14sp"
-            tools:text="@string/message_sending" />
-    </LinearLayout>
-</LinearLayout>
diff --git a/ring-android/app/src/main/res/layout/item_conv_image_me.xml b/ring-android/app/src/main/res/layout/item_conv_image_me.xml
new file mode 100644
index 000000000..3111cf34e
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/item_conv_image_me.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2004-2019 Savoir-faire Linux Inc.
+  ~
+  ~ Author: Pierre Duchemin <pierre.duchemin@savoirfairelinux.com>
+  ~
+  ~ 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, write to the Free Software
+  ~  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="8dp"
+    android:clickable="true"
+    android:descendantFocusability="blocksDescendants"
+    android:focusable="true"
+    android:orientation="vertical"
+    android:gravity="center">
+
+    <TextView
+        android:id="@+id/msg_details_txt_perm"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:textColor="@color/textColorSecondary"
+        android:textSize="14sp"
+        android:paddingTop="4dp"
+        tools:text="@string/time_just_now" />
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="8dp"
+        android:paddingEnd="2dp"
+        android:paddingStart="72dp">
+
+        <LinearLayout
+            android:id="@+id/imageLayout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="end"
+            android:layout_alignParentEnd="true"
+            android:orientation="vertical">
+
+            <RelativeLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:minWidth="200dp">
+
+                <cx.ring.views.ConversationImageView
+                    android:id="@+id/image"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginEnd="24dp"
+                    android:layout_alignParentEnd="true"
+                    android:adjustViewBounds="true"
+                    android:maxHeight="200dp"
+                    android:minWidth="@dimen/conversation_avatar_size"
+                    android:minHeight="@dimen/conversation_avatar_size"
+                    android:transitionName="picture"
+                    tools:src="@drawable/ic_jami"/>
+
+                <ImageView
+                    android:id="@+id/status_icon"
+                    android:layout_width="14dp"
+                    android:layout_height="14dp"
+                    android:layout_alignBottom="@id/image"
+                    android:layout_alignParentEnd="true"
+                    android:layout_marginEnd="5dp"
+                    android:tint="@color/grey_500"
+                    app:srcCompat="@drawable/round_check_circle_24" />
+
+                <TextView
+                    android:id="@+id/msg_details_txt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_alignEnd="@id/image"
+                    android:layout_below="@id/image"
+                    android:layout_marginEnd="@dimen/conversation_message_radius"
+                    android:textColor="@color/textColorSecondary"
+                    android:textSize="12sp"
+                    android:singleLine="true"
+                    tools:text="Tue" />
+
+            </RelativeLayout>
+
+        </LinearLayout>
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/ring-android/app/src/main/res/layout/item_conv_image_peer.xml b/ring-android/app/src/main/res/layout/item_conv_image_peer.xml
new file mode 100644
index 000000000..6557a927f
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/item_conv_image_peer.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2004-2019 Savoir-faire Linux Inc.
+  ~
+  ~ Author: Pierre Duchemin <pierre.duchemin@savoirfairelinux.com>
+  ~
+  ~ 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, write to the Free Software
+  ~  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="8dp"
+    android:clickable="true"
+    android:descendantFocusability="blocksDescendants"
+    android:focusable="true"
+    android:orientation="vertical"
+    android:gravity="center">
+
+    <TextView
+        android:id="@+id/msg_details_txt_perm"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:textColor="@color/textColorSecondary"
+        android:textSize="14sp"
+        android:paddingTop="4dp"
+        tools:text="@string/time_just_now" />
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="8dp"
+        android:paddingStart="@dimen/padding_medium"
+        android:paddingEnd="@dimen/padding_large">
+
+        <ImageView
+            android:id="@+id/photo"
+            android:layout_width="@dimen/conversation_avatar_size"
+            android:layout_height="@dimen/conversation_avatar_size"
+            android:background="@null"
+            android:gravity="start"
+            android:layout_alignBottom="@id/imgLayout"
+            android:scaleType="centerCrop"/>
+
+        <LinearLayout
+            android:id="@+id/imgLayout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/padding_medium"
+            android:layout_marginEnd="20dp"
+            android:minHeight="@dimen/conversation_avatar_size"
+            android:layout_toEndOf="@+id/photo"
+            android:gravity="center_vertical">
+
+            <cx.ring.views.ConversationImageView
+                android:id="@+id/image"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:adjustViewBounds="true"
+                android:maxHeight="200dp"
+                android:minWidth="@dimen/conversation_avatar_size"
+                android:scaleType="centerCrop"
+                android:transitionName="picture"
+                tools:src="@drawable/ic_jami" />
+
+        </LinearLayout>
+
+        <TextView
+            android:id="@+id/msg_details_txt"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignStart="@+id/imgLayout"
+            android:layout_below="@+id/imgLayout"
+            android:layout_marginStart="@dimen/conversation_message_radius"
+            android:textColor="@color/textColorSecondary"
+            android:textSize="12sp"
+            tools:text="@string/time_just_now" />
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/ring-android/app/src/main/res/layout/item_conv_msg_peer.xml b/ring-android/app/src/main/res/layout/item_conv_msg_peer.xml
index ee3b4ecbe..81584e8ae 100644
--- a/ring-android/app/src/main/res/layout/item_conv_msg_peer.xml
+++ b/ring-android/app/src/main/res/layout/item_conv_msg_peer.xml
@@ -69,7 +69,7 @@ along with this program; if not, write to the Free Software
             android:id="@+id/msg_txt"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_toRightOf="@+id/photo"
+            android:layout_toEndOf="@+id/photo"
             android:layout_marginStart="@dimen/padding_medium"
             android:layout_marginEnd="20dp"
             android:minWidth="@dimen/conversation_avatar_size"
diff --git a/ring-android/app/src/main/res/layout/item_conv_video.xml b/ring-android/app/src/main/res/layout/item_conv_video_me.xml
similarity index 60%
rename from ring-android/app/src/main/res/layout/item_conv_video.xml
rename to ring-android/app/src/main/res/layout/item_conv_video_me.xml
index b0cf7fe85..830c944b6 100644
--- a/ring-android/app/src/main/res/layout/item_conv_video.xml
+++ b/ring-android/app/src/main/res/layout/item_conv_video_me.xml
@@ -17,34 +17,43 @@
   ~ along with this program; if not, write to the Free Software
   ~  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_marginTop="8dp"
-    android:layout_marginBottom="8dp"
+    android:paddingBottom="8dp"
+    android:background="@android:color/transparent"
     android:clickable="true"
     android:descendantFocusability="blocksDescendants"
     android:focusable="true"
     android:orientation="vertical"
-    android:paddingStart="@dimen/padding_large"
-    android:paddingLeft="@dimen/padding_large"
-    android:paddingEnd="@dimen/padding_large"
-    android:paddingRight="@dimen/padding_large">
+    android:gravity="center">
 
-    <LinearLayout
-        android:id="@+id/imageLayout"
+    <TextView
+        android:id="@+id/msg_details_txt_perm"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="end"
-        android:orientation="vertical"
-        android:paddingStart="56dp">
+        android:visibility="gone"
+        android:textColor="@color/textColorSecondary"
+        android:textSize="14sp"
+        android:paddingTop="4dp"
+        tools:text="@string/time_just_now" />
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="8dp"
+        android:paddingEnd="2dp"
+        android:paddingStart="72dp">
 
         <androidx.cardview.widget.CardView
             android:id="@+id/video_frame"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:layout_marginEnd="24dp"
             android:foreground="@drawable/baseline_play_arrow_24"
             android:foregroundGravity="center"
             app:cardCornerRadius="16dp">
@@ -56,16 +65,31 @@
                 android:adjustViewBounds="true"
                 android:maxHeight="200dp"
                 android:transitionName="picture" />
+
         </androidx.cardview.widget.CardView>
 
+        <ImageView
+            android:id="@+id/status_icon"
+            android:layout_width="14dp"
+            android:layout_height="14dp"
+            android:layout_alignBottom="@id/video_frame"
+            android:layout_alignParentEnd="true"
+            android:layout_marginEnd="5dp"
+            android:tint="@color/grey_500"
+            app:srcCompat="@drawable/round_check_circle_24" />
+
         <TextView
             android:id="@+id/msg_details_txt"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginBottom="4dp"
+            android:layout_alignEnd="@id/video_frame"
+            android:layout_below="@id/video_frame"
+            android:layout_marginEnd="@dimen/conversation_message_radius"
             android:textColor="@color/textColorSecondary"
-            android:textSize="14sp"
-            tools:text="@string/message_sending" />
-    </LinearLayout>
+            android:textSize="12sp"
+            android:singleLine="true"
+            tools:text="@string/time_just_now" />
+
+    </RelativeLayout>
 
 </LinearLayout>
diff --git a/ring-android/app/src/main/res/layout/item_conv_video_peer.xml b/ring-android/app/src/main/res/layout/item_conv_video_peer.xml
new file mode 100644
index 000000000..e68a644c6
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/item_conv_video_peer.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2004-2019 Savoir-faire Linux Inc.
+  ~
+  ~ Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+  ~
+  ~ 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, write to the Free Software
+  ~  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="8dp"
+    android:background="@android:color/transparent"
+    android:clickable="true"
+    android:descendantFocusability="blocksDescendants"
+    android:focusable="true"
+    android:orientation="vertical"
+    android:gravity="center">
+
+    <TextView
+        android:id="@+id/msg_details_txt_perm"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:textColor="@color/textColorSecondary"
+        android:textSize="14sp"
+        android:paddingTop="4dp"
+        tools:text="@string/time_just_now" />
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="8dp"
+        android:paddingStart="@dimen/padding_medium"
+        android:paddingEnd="@dimen/padding_large">
+
+        <ImageView
+            android:id="@+id/photo"
+            android:layout_width="@dimen/conversation_avatar_size"
+            android:layout_height="@dimen/conversation_avatar_size"
+            android:background="@null"
+            android:gravity="start"
+            android:layout_alignBottom="@id/video_frame"
+            android:scaleType="centerCrop"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/video_frame"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/padding_medium"
+            android:layout_marginEnd="20dp"
+            android:minHeight="@dimen/conversation_avatar_size"
+            android:layout_toEndOf="@+id/photo"
+            android:foreground="@drawable/baseline_play_arrow_24"
+            android:foregroundGravity="center"
+            app:cardCornerRadius="16dp">
+
+            <TextureView
+                android:id="@+id/video"
+                android:layout_width="200dp"
+                android:layout_height="200dp"
+                android:adjustViewBounds="true"
+                android:maxHeight="200dp"
+                android:transitionName="picture" />
+
+        </androidx.cardview.widget.CardView>
+
+        <TextView
+            android:id="@+id/msg_details_txt"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignStart="@id/video_frame"
+            android:layout_below="@id/video_frame"
+            android:layout_marginStart="@dimen/conversation_message_radius"
+            android:textColor="@color/textColorSecondary"
+            android:textSize="12sp"
+            android:singleLine="true"
+            tools:text="@string/time_just_now" />
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/ring-android/libringclient/src/main/java/cx/ring/model/DataTransfer.java b/ring-android/libringclient/src/main/java/cx/ring/model/DataTransfer.java
index 92fc514e7..27a7b06c0 100644
--- a/ring-android/libringclient/src/main/java/cx/ring/model/DataTransfer.java
+++ b/ring-android/libringclient/src/main/java/cx/ring/model/DataTransfer.java
@@ -42,7 +42,6 @@ public class DataTransfer extends Interaction {
         mAuthor = isOutgoing ? null : conversation.getParticipant();
         mAccount = account;
         mConversation = conversation;
-        mIsIncoming = !isOutgoing;
         mPeerId = conversation.getParticipant();
         mTotalSize = totalSize;
         mBytesProgress = bytesProgress;
@@ -52,6 +51,7 @@ public class DataTransfer extends Interaction {
         mTimestamp = System.currentTimeMillis();
         mIsRead = 1;
         mDaemonId = daemonId;
+        mIsIncoming = mAuthor != null;
     }
 
 
@@ -60,7 +60,6 @@ public class DataTransfer extends Interaction {
         mDaemonId = interaction.getDaemonId();
         mAuthor = interaction.getAuthor();
         mConversation = interaction.getConversation();
-        mIsIncoming = interaction.isIncoming();
         mPeerId = interaction.getConversation().getParticipant();
         mBody = interaction.getBody();
         mStatus = interaction.getStatus().toString();
@@ -69,6 +68,7 @@ public class DataTransfer extends Interaction {
         mAccount = interaction.getAccount();
         mContact = interaction.getContact();
         mIsRead = 1;
+        mIsIncoming = mAuthor != null;
     }
 
     public String getExtension() {
-- 
GitLab