aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/Activity/ViewPrivateMessagesActivity.java51
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/Adapter/PrivateMessagesDetailRecyclerViewAdapter.java190
-rw-r--r--app/src/main/res/drawable/private_message_ballon_received.xml11
-rw-r--r--app/src/main/res/drawable/private_message_ballon_sent.xml11
-rw-r--r--app/src/main/res/layout/activity_view_private_messages.xml8
-rw-r--r--app/src/main/res/layout/item_private_message_received.xml61
-rw-r--r--app/src/main/res/layout/item_private_message_sent.xml43
7 files changed, 308 insertions, 67 deletions
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/Activity/ViewPrivateMessagesActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/Activity/ViewPrivateMessagesActivity.java
index 1a98eec0..c5a78e1a 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/Activity/ViewPrivateMessagesActivity.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/Activity/ViewPrivateMessagesActivity.java
@@ -13,10 +13,11 @@ import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import androidx.transition.AutoTransition;
+import androidx.transition.TransitionManager;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
-import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.r0adkll.slidr.Slidr;
import javax.inject.Inject;
@@ -27,6 +28,7 @@ import butterknife.ButterKnife;
import ml.docilealligator.infinityforreddit.ActivityToolbarInterface;
import ml.docilealligator.infinityforreddit.Adapter.PrivateMessagesDetailRecyclerViewAdapter;
import ml.docilealligator.infinityforreddit.AsyncTask.GetCurrentAccountAsyncTask;
+import ml.docilealligator.infinityforreddit.AsyncTask.LoadUserDataAsyncTask;
import ml.docilealligator.infinityforreddit.CustomTheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.Infinity;
import ml.docilealligator.infinityforreddit.Message;
@@ -40,6 +42,8 @@ public class ViewPrivateMessagesActivity extends BaseActivity implements Activit
public static final String EXTRA_PRIVATE_MESSAGE = "EPM";
private static final String NULL_ACCESS_TOKEN_STATE = "NATS";
private static final String ACCESS_TOKEN_STATE = "ATS";
+ private static final String ACCOUNT_NAME_STATE = "ANS";
+ private static final String USER_AVATAR_STATE = "UAS";
@BindView(R.id.coordinator_layout_view_private_messages_activity)
CoordinatorLayout mCoordinatorLayout;
@BindView(R.id.collapsing_toolbar_layout_view_private_messages_activity)
@@ -50,8 +54,6 @@ public class ViewPrivateMessagesActivity extends BaseActivity implements Activit
Toolbar mToolbar;
@BindView(R.id.recycler_view_view_private_messages)
RecyclerView mRecyclerView;
- @BindView(R.id.fab_view_private_messages_activity)
- FloatingActionButton mFab;
@Inject
@Named("oauth")
Retrofit mOauthRetrofit;
@@ -67,6 +69,8 @@ public class ViewPrivateMessagesActivity extends BaseActivity implements Activit
private Message privateMessage;
private boolean mNullAccessToken = false;
private String mAccessToken;
+ private String mAccountName;
+ private String mUserAvatar;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -98,9 +102,6 @@ public class ViewPrivateMessagesActivity extends BaseActivity implements Activit
int navBarHeight = getNavBarHeight();
if (navBarHeight > 0) {
mRecyclerView.setPadding(0, 0, 0, navBarHeight);
- CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) mFab.getLayoutParams();
- params.bottomMargin = navBarHeight;
- mFab.setLayoutParams(params);
}
}
}
@@ -109,7 +110,7 @@ public class ViewPrivateMessagesActivity extends BaseActivity implements Activit
privateMessage = intent.getParcelableExtra(EXTRA_PRIVATE_MESSAGE);
if (privateMessage != null) {
- mToolbar.setTitle(privateMessage.getTitle());
+ mToolbar.setTitle(privateMessage.getSubject());
}
setSupportActionBar(mToolbar);
setToolbarGoToTop(mToolbar);
@@ -117,6 +118,8 @@ public class ViewPrivateMessagesActivity extends BaseActivity implements Activit
if (savedInstanceState != null) {
mNullAccessToken = savedInstanceState.getBoolean(NULL_ACCESS_TOKEN_STATE);
mAccessToken = savedInstanceState.getString(ACCESS_TOKEN_STATE);
+ mAccountName = savedInstanceState.getString(ACCOUNT_NAME_STATE);
+ mUserAvatar = savedInstanceState.getString(USER_AVATAR_STATE);
if (!mNullAccessToken && mAccessToken == null) {
getCurrentAccountAndBindView();
@@ -134,20 +137,34 @@ public class ViewPrivateMessagesActivity extends BaseActivity implements Activit
mNullAccessToken = true;
} else {
mAccessToken = account.getAccessToken();
+ mAccountName = account.getUsername();
}
bindView();
}).execute();
}
private void bindView() {
- mAdapter = new PrivateMessagesDetailRecyclerViewAdapter(this, privateMessage, mCustomThemeWrapper);
+ mAdapter = new PrivateMessagesDetailRecyclerViewAdapter(this, privateMessage, mAccountName, mCustomThemeWrapper);
mLinearLayoutManager = new LinearLayoutManager(this);
- mLinearLayoutManager.setReverseLayout(true);
- mLinearLayoutManager.setStackFromEnd(true);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mRecyclerView.setAdapter(mAdapter);
}
+ public void fetchUserAvatar(String username, ProvideUserAvatarCallback provideUserAvatarCallback) {
+ if (mUserAvatar == null) {
+ new LoadUserDataAsyncTask(mRedditDataRoomDatabase.userDao(), username, mOauthRetrofit, iconImageUrl -> {
+ mUserAvatar = iconImageUrl;
+ provideUserAvatarCallback.fetchAvatarSuccess(iconImageUrl);
+ }).execute();
+ } else {
+ provideUserAvatarCallback.fetchAvatarSuccess(mUserAvatar);
+ }
+ }
+
+ public void delayTransition() {
+ TransitionManager.beginDelayedTransition(mRecyclerView, new AutoTransition());
+ }
+
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
@@ -159,6 +176,15 @@ public class ViewPrivateMessagesActivity extends BaseActivity implements Activit
}
@Override
+ protected void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(NULL_ACCESS_TOKEN_STATE, mNullAccessToken);
+ outState.putString(ACCESS_TOKEN_STATE, mAccessToken);
+ outState.putString(ACCOUNT_NAME_STATE, mAccountName);
+ outState.putString(USER_AVATAR_STATE, mUserAvatar);
+ }
+
+ @Override
protected SharedPreferences getDefaultSharedPreferences() {
return mSharedPreferences;
}
@@ -172,7 +198,6 @@ public class ViewPrivateMessagesActivity extends BaseActivity implements Activit
protected void applyCustomTheme() {
mCoordinatorLayout.setBackgroundColor(mCustomThemeWrapper.getBackgroundColor());
applyAppBarLayoutAndToolbarTheme(mAppBarLayout, mToolbar);
- applyFABTheme(mFab);
}
@Override
@@ -181,4 +206,8 @@ public class ViewPrivateMessagesActivity extends BaseActivity implements Activit
mLinearLayoutManager.scrollToPositionWithOffset(0, 0);
}
}
+
+ public interface ProvideUserAvatarCallback {
+ void fetchAvatarSuccess(String userAvatarUrl);
+ }
} \ No newline at end of file
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/Adapter/PrivateMessagesDetailRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/Adapter/PrivateMessagesDetailRecyclerViewAdapter.java
index 821d0e08..270d89ac 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/Adapter/PrivateMessagesDetailRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/Adapter/PrivateMessagesDetailRecyclerViewAdapter.java
@@ -1,18 +1,24 @@
package ml.docilealligator.infinityforreddit.Adapter;
-import android.content.Context;
import android.content.Intent;
+import android.graphics.Color;
import android.net.Uri;
import android.text.style.SuperscriptSpan;
import android.text.util.Linkify;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.RequestManager;
+import com.bumptech.glide.request.RequestOptions;
+
import butterknife.BindView;
import butterknife.ButterKnife;
import io.noties.markwon.AbstractMarkwonPlugin;
@@ -21,38 +27,46 @@ import io.noties.markwon.MarkwonConfiguration;
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin;
import io.noties.markwon.linkify.LinkifyPlugin;
import io.noties.markwon.simple.ext.SimpleExtPlugin;
+import jp.wasabeef.glide.transformations.RoundedCornersTransformation;
import ml.docilealligator.infinityforreddit.Activity.LinkResolverActivity;
+import ml.docilealligator.infinityforreddit.Activity.ViewPrivateMessagesActivity;
import ml.docilealligator.infinityforreddit.Activity.ViewUserDetailActivity;
import ml.docilealligator.infinityforreddit.CustomTheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.Message;
import ml.docilealligator.infinityforreddit.R;
+import ml.docilealligator.infinityforreddit.Utils.Utils;
public class PrivateMessagesDetailRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+ private static final int VIEW_TYPE_MESSAGE_SENT = 0;
+ private static final int VIEW_TYPE_MESSAGE_RECEIVED = 1;
private Message mMessage;
- private Context mContext;
+ private ViewPrivateMessagesActivity mViewPrivateMessagesActivity;
+ private RequestManager mGlide;
+ private String mAccountName;
private Markwon mMarkwon;
private int mMessageBackgroundColor;
- private int mUsernameColor;
- private int mPrimaryTextColor;
private int mSecondaryTextColor;
private int mUnreadMessageBackgroundColor;
- public PrivateMessagesDetailRecyclerViewAdapter(Context context, Message message, CustomThemeWrapper customThemeWrapper) {
+ public PrivateMessagesDetailRecyclerViewAdapter(ViewPrivateMessagesActivity viewPrivateMessagesActivity, Message message, String accountName,
+ CustomThemeWrapper customThemeWrapper) {
mMessage = message;
- mContext = context;
- mMarkwon = Markwon.builder(mContext)
+ mViewPrivateMessagesActivity = viewPrivateMessagesActivity;
+ mGlide = Glide.with(viewPrivateMessagesActivity);
+ mAccountName = accountName;
+ mMarkwon = Markwon.builder(viewPrivateMessagesActivity)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
builder.linkResolver((view, link) -> {
- Intent intent = new Intent(mContext, LinkResolverActivity.class);
+ Intent intent = new Intent(viewPrivateMessagesActivity, LinkResolverActivity.class);
Uri uri = Uri.parse(link);
if (uri.getScheme() == null && uri.getHost() == null) {
intent.setData(LinkResolverActivity.getRedditUriByPath(link));
} else {
intent.setData(uri);
}
- mContext.startActivity(intent);
+ viewPrivateMessagesActivity.startActivity(intent);
});
}
})
@@ -66,46 +80,91 @@ public class PrivateMessagesDetailRecyclerViewAdapter extends RecyclerView.Adapt
)
.build();
mMessageBackgroundColor = customThemeWrapper.getCardViewBackgroundColor();
- mUsernameColor = customThemeWrapper.getUsername();
- mPrimaryTextColor = customThemeWrapper.getPrimaryTextColor();
mSecondaryTextColor = customThemeWrapper.getSecondaryTextColor();
mUnreadMessageBackgroundColor = customThemeWrapper.getUnreadMessageBackgroundColor();
}
+ @Override
+ public int getItemViewType(int position) {
+ if (position == 0) {
+ return mMessage.getAuthor().equals(mAccountName) ? VIEW_TYPE_MESSAGE_SENT : VIEW_TYPE_MESSAGE_RECEIVED;
+ } else {
+ return mMessage.getReplies().get(position - 1).getAuthor().equals(mAccountName) ? VIEW_TYPE_MESSAGE_SENT : VIEW_TYPE_MESSAGE_RECEIVED;
+ }
+ }
+
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- return new DataViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_message, parent, false));
+ if (viewType == VIEW_TYPE_MESSAGE_SENT) {
+ return new SentMessageViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_private_message_sent, parent, false));
+ } else {
+ return new ReceivedMessageViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_private_message_received, parent, false));
+ }
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
- if (holder instanceof DataViewHolder) {
- Message message;
- if (holder.getAdapterPosition() == 0) {
- message = mMessage;
- } else {
- message = mMessage.getReplies().get(holder.getAdapterPosition() - 1);
- }
-
- if (message != null) {
+ Message message;
+ if (holder.getAdapterPosition() == 0) {
+ message = mMessage;
+ } else {
+ message = mMessage.getReplies().get(holder.getAdapterPosition() - 1);
+ }
+ if (message != null) {
+ if (holder instanceof MessageViewHolder) {
if (message.isNew()) {
- ((DataViewHolder) holder).itemView.setBackgroundColor(
+ ((MessageViewHolder) holder).itemView.setBackgroundColor(
mUnreadMessageBackgroundColor);
}
+ mMarkwon.setMarkdown(((MessageViewHolder) holder).messageTextView, message.getBody());
- ((DataViewHolder) holder).authorTextView.setText(message.getAuthor());
- String subject = message.getSubject().substring(0, 1).toUpperCase() + message.getSubject().substring(1);
- ((DataViewHolder) holder).subjectTextView.setText(subject);
- mMarkwon.setMarkdown(((DataViewHolder) holder).contentCustomMarkwonView, message.getBody());
+ ((MessageViewHolder) holder).messageTextView.setOnClickListener(view -> ((MessageViewHolder) holder).itemView.performClick());
- ((DataViewHolder) holder).authorTextView.setOnClickListener(view -> {
- Intent intent = new Intent(mContext, ViewUserDetailActivity.class);
- intent.putExtra(ViewUserDetailActivity.EXTRA_USER_NAME_KEY, message.getAuthor());
- mContext.startActivity(intent);
+ ((MessageViewHolder) holder).messageTextView.setOnClickListener(view -> {
+ Log.i("asfasdf", "asdf " + ((MessageViewHolder) holder).timeTextView.getHeight());
+ if (((MessageViewHolder) holder).timeTextView.getVisibility() != View.VISIBLE) {
+ ((MessageViewHolder) holder).timeTextView.setVisibility(View.VISIBLE);
+ mViewPrivateMessagesActivity.delayTransition();
+ } else {
+ ((MessageViewHolder) holder).timeTextView.setVisibility(View.GONE);
+ mViewPrivateMessagesActivity.delayTransition();
+ }
+ /*if (((MessageViewHolder) holder).timeTextView.getHeight() == 0) {
+ ((MessageViewHolder) holder).timeTextView.getLayoutParams().height = ConstraintLayout.LayoutParams.WRAP_CONTENT;
+ mViewPrivateMessagesActivity.delayTransition();
+ } else {
+ mViewPrivateMessagesActivity.delayTransition();
+ ((MessageViewHolder) holder).timeTextView.getLayoutParams().height = 0;
+ }*/
+ });
+
+ ((MessageViewHolder) holder).timeTextView.setText(Utils.getElapsedTime(mViewPrivateMessagesActivity, message.getTimeUTC()));
+ }
+
+ if (holder instanceof SentMessageViewHolder) {
+ ((SentMessageViewHolder) holder).messageTextView.setBackgroundResource(R.drawable.private_message_ballon_sent);
+ } else if (holder instanceof ReceivedMessageViewHolder) {
+ mViewPrivateMessagesActivity.fetchUserAvatar(message.getAuthor(), userAvatarUrl -> {
+ if (userAvatarUrl == null || userAvatarUrl.equals("")) {
+ mGlide.load(R.drawable.subreddit_default_icon)
+ .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0)))
+ .into(((ReceivedMessageViewHolder) holder).userAvatarImageView);
+ } else {
+ mGlide.load(userAvatarUrl)
+ .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0)))
+ .error(mGlide.load(R.drawable.subreddit_default_icon)
+ .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0))))
+ .into(((ReceivedMessageViewHolder) holder).userAvatarImageView);
+ }
});
- ((DataViewHolder) holder).contentCustomMarkwonView.setOnClickListener(view -> ((DataViewHolder) holder).itemView.performClick());
+ ((ReceivedMessageViewHolder) holder).userAvatarImageView.setOnClickListener(view -> {
+ Intent intent = new Intent(mViewPrivateMessagesActivity, ViewUserDetailActivity.class);
+ intent.putExtra(ViewUserDetailActivity.EXTRA_USER_NAME_KEY, message.getAuthor());
+ mViewPrivateMessagesActivity.startActivity(intent);
+ });
+ ((ReceivedMessageViewHolder) holder).messageTextView.setBackgroundResource(R.drawable.private_message_ballon_received);
}
}
}
@@ -138,27 +197,62 @@ public class PrivateMessagesDetailRecyclerViewAdapter extends RecyclerView.Adapt
notifyItemInserted(currentSize);
}
- class DataViewHolder extends RecyclerView.ViewHolder {
- @BindView(R.id.author_text_view_item_message)
- TextView authorTextView;
- @BindView(R.id.subject_text_view_item_message)
- TextView subjectTextView;
- @BindView(R.id.title_text_view_item_message)
- TextView titleTextView;
- @BindView(R.id.content_custom_markwon_view_item_message)
- TextView contentCustomMarkwonView;
-
- DataViewHolder(View itemView) {
+ @Override
+ public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
+ super.onViewRecycled(holder);
+ if (holder instanceof MessageViewHolder) {
+ ((MessageViewHolder) holder).messageTextView.setBackground(null);
+ //((MessageViewHolder) holder).timeTextView.getLayoutParams().height = 0;
+ ((MessageViewHolder) holder).timeTextView.setVisibility(View.GONE);
+ }
+ if (holder instanceof ReceivedMessageViewHolder) {
+ mGlide.clear(((ReceivedMessageViewHolder) holder).userAvatarImageView);
+ }
+ }
+
+ class MessageViewHolder extends RecyclerView.ViewHolder {
+ TextView messageTextView;
+ TextView timeTextView;
+
+ public MessageViewHolder(@NonNull View itemView) {
super(itemView);
- ButterKnife.bind(this, itemView);
+ }
- titleTextView.setVisibility(View.GONE);
+ void setBaseView(TextView messageTextView, TextView timeTextView) {
+ this.messageTextView = messageTextView;
+ this.timeTextView = timeTextView;
itemView.setBackgroundColor(mMessageBackgroundColor);
- authorTextView.setTextColor(mUsernameColor);
- subjectTextView.setTextColor(mPrimaryTextColor);
- titleTextView.setTextColor(mPrimaryTextColor);
- contentCustomMarkwonView.setTextColor(mSecondaryTextColor);
+ messageTextView.setTextColor(Color.WHITE);
+ timeTextView.setTextColor(mSecondaryTextColor);
+ }
+ }
+
+ class SentMessageViewHolder extends MessageViewHolder {
+ @BindView(R.id.message_text_view_item_private_message_sent)
+ TextView messageTextView;
+ @BindView(R.id.time_text_view_item_private_message_sent)
+ TextView timeTextView;
+
+ SentMessageViewHolder(View itemView) {
+ super(itemView);
+ ButterKnife.bind(this, itemView);
+ setBaseView(messageTextView, timeTextView);
+ }
+ }
+
+ class ReceivedMessageViewHolder extends MessageViewHolder {
+ @BindView(R.id.avatar_image_view_item_private_message_received)
+ ImageView userAvatarImageView;
+ @BindView(R.id.message_text_view_item_private_message_received)
+ TextView messageTextView;
+ @BindView(R.id.time_text_view_item_private_message_received)
+ TextView timeTextView;
+
+ ReceivedMessageViewHolder(View itemView) {
+ super(itemView);
+ ButterKnife.bind(this, itemView);
+ setBaseView(messageTextView, timeTextView);
}
}
}
diff --git a/app/src/main/res/drawable/private_message_ballon_received.xml b/app/src/main/res/drawable/private_message_ballon_received.xml
new file mode 100644
index 00000000..522d5cf0
--- /dev/null
+++ b/app/src/main/res/drawable/private_message_ballon_received.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid
+ android:color="#4185F4" >
+ </solid>
+
+ <corners
+ android:radius="22dp">
+ </corners>
+</shape>
diff --git a/app/src/main/res/drawable/private_message_ballon_sent.xml b/app/src/main/res/drawable/private_message_ballon_sent.xml
new file mode 100644
index 00000000..e813c552
--- /dev/null
+++ b/app/src/main/res/drawable/private_message_ballon_sent.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid
+ android:color="#31BF7D" >
+ </solid>
+
+ <corners
+ android:radius="22dp">
+ </corners>
+</shape> \ No newline at end of file
diff --git a/app/src/main/res/layout/activity_view_private_messages.xml b/app/src/main/res/layout/activity_view_private_messages.xml
index 67c665a9..3819bc4d 100644
--- a/app/src/main/res/layout/activity_view_private_messages.xml
+++ b/app/src/main/res/layout/activity_view_private_messages.xml
@@ -40,12 +40,4 @@
android:clipToPadding="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
- <com.google.android.material.floatingactionbutton.FloatingActionButton
- android:id="@+id/fab_view_private_messages_activity"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|end"
- android:layout_margin="@dimen/fab_margin"
- app:srcCompat="@drawable/ic_add_day_night_24dp" />
-
</androidx.coordinatorlayout.widget.CoordinatorLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/item_private_message_received.xml b/app/src/main/res/layout/item_private_message_received.xml
new file mode 100644
index 00000000..f3ba1c88
--- /dev/null
+++ b/app/src/main/res/layout/item_private_message_received.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:animateLayoutChanges="true">
+
+ <pl.droidsonroids.gif.GifImageView
+ android:id="@+id/avatar_image_view_item_private_message_received"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/barrier3" />
+
+ <TextView
+ android:id="@+id/message_text_view_item_private_message_received"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:layout_marginStart="16dp"
+ android:textSize="?attr/content_font_16"
+ app:layout_constraintBottom_toTopOf="@id/time_text_view_item_private_message_received"
+ app:layout_constraintStart_toEndOf="@id/avatar_image_view_item_private_message_received"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintWidth_max="wrap"
+ app:layout_constraintWidth_percent="0.7"
+ app:layout_constraintHorizontal_bias="0" />
+
+ <TextView
+ android:id="@+id/time_text_view_item_private_message_received"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:textSize="?attr/font_default"
+ android:visibility="gone"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/avatar_image_view_item_private_message_received"
+ app:layout_constraintTop_toBottomOf="@id/message_text_view_item_private_message_received"
+ app:layout_constraintWidth_max="wrap"
+ app:layout_constraintWidth_percent="0.7"
+ app:layout_constraintHorizontal_bias="0" />
+
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/barrier3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierDirection="start"
+ app:constraint_referenced_ids="message_text_view_item_private_message_received, time_text_view_item_private_message_received" />
+
+</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/item_private_message_sent.xml b/app/src/main/res/layout/item_private_message_sent.xml
new file mode 100644
index 00000000..62951ba4
--- /dev/null
+++ b/app/src/main/res/layout/item_private_message_sent.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:animateLayoutChanges="true">
+
+ <TextView
+ android:id="@+id/message_text_view_item_private_message_sent"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:textSize="?attr/content_font_16"
+ app:layout_constraintBottom_toTopOf="@id/time_text_view_item_private_message_sent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintWidth_max="wrap"
+ app:layout_constraintWidth_percent="0.7"
+ app:layout_constraintHorizontal_bias="1" />
+
+ <TextView
+ android:id="@+id/time_text_view_item_private_message_sent"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:textSize="?attr/font_default"
+ android:visibility="gone"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/message_text_view_item_private_message_sent"
+ app:layout_constraintWidth_max="wrap"
+ app:layout_constraintWidth_percent="0.7"
+ app:layout_constraintHorizontal_bias="1" />
+
+</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file