From 41233a837b1b970370c5629e4b7c3f2732794ffd Mon Sep 17 00:00:00 2001
From: Konrad Pozniak <connyduck@users.noreply.github.com>
Date: Thu, 30 Nov 2017 20:12:09 +0100
Subject: [PATCH] UI Improvements (#445)

UI Improvements
---
 .../keylesspalace/tusky/AccountActivity.java  |   4 +-
 .../keylesspalace/tusky/ComposeActivity.java  |  12 +-
 .../keylesspalace/tusky/SearchActivity.java   |   8 +-
 .../tusky/adapter/NotificationsAdapter.java   | 162 ++++---
 .../tusky/adapter/StatusBaseViewHolder.java   | 278 +++++-------
 .../adapter/StatusDetailedViewHolder.java     |  45 +-
 .../tusky/adapter/StatusViewHolder.java       |  15 +-
 .../tusky/adapter/ThreadAdapter.java          |   7 +-
 .../tusky/entity/Attachment.java              |  79 ++++
 .../com/keylesspalace/tusky/entity/Media.java |  32 --
 .../keylesspalace/tusky/entity/Status.java    |  48 +--
 .../tusky/fragment/AccountMediaFragment.kt    |  19 +-
 .../tusky/fragment/NotificationsFragment.java |  42 +-
 .../tusky/fragment/SFragment.java             |   3 +-
 .../tusky/fragment/TimelineFragment.java      |  15 +-
 .../tusky/fragment/ViewThreadFragment.java    |  56 ++-
 .../interfaces/StatusActionListener.java      |   4 +-
 .../tusky/network/MastodonApi.java            |   4 +-
 .../tusky/util/CustomEmojiHelper.java         | 137 ++++++
 .../tusky/util/CustomTabURLSpan.java          |  36 --
 .../tusky/util/CustomURLSpan.java             |  41 ++
 .../keylesspalace/tusky/util/LinkHelper.java  |  22 +-
 .../keylesspalace/tusky/util/OkHttpUtils.java |   4 +-
 .../tusky/util/ViewDataUtils.java             |  40 +-
 .../view/ConversationLineItemDecoration.java  |  21 +-
 .../tusky/viewdata/NotificationViewData.java  |   8 +-
 .../tusky/viewdata/StatusViewData.java        |  29 +-
 .../drawable/conversation_divider_dark.xml    |   7 -
 .../conversation_thread_line_dark.xml         |   6 +
 .../conversation_thread_line_light.xml        |   6 +
 .../res/drawable/favourite_inactive_dark.xml  |  14 +-
 .../res/drawable/favourite_inactive_light.xml |  15 +-
 .../main/res/drawable/ic_person_add_24dp.xml  |   2 +-
 app/src/main/res/drawable/ic_play_48dp.xml    |   9 -
 .../res/drawable/ic_play_indicator_dark.xml   |   8 +
 .../res/drawable/ic_play_indicator_light.xml  |  15 +
 .../main/res/drawable/ic_reblog_dark_18dp.xml |   4 +
 .../res/drawable/ic_reblog_light_18dp.xml     |   4 +
 app/src/main/res/drawable/ic_reply_24dp.xml   |  12 +-
 .../main/res/drawable/ic_reply_all_24dp.xml   |  10 +
 app/src/main/res/layout/activity_account.xml  |  26 +-
 app/src/main/res/layout/activity_compose.xml  |   3 +-
 app/src/main/res/layout/item_follow.xml       | 133 +++---
 app/src/main/res/layout/item_status.xml       | 398 +++++++++---------
 .../main/res/layout/item_status_detailed.xml  | 322 +++++++-------
 .../res/layout/item_status_notification.xml   | 106 +++--
 app/src/main/res/values-de/strings.xml        |   4 +-
 app/src/main/res/values-pl/strings.xml        |   3 +-
 app/src/main/res/values/attrs.xml             |   4 +
 app/src/main/res/values/dimens.xml            |   9 +-
 app/src/main/res/values/donottranslate.xml    |   2 +
 app/src/main/res/values/strings.xml           |   3 +-
 app/src/main/res/values/styles.xml            |  11 +
 app/src/main/res/xml/preferences.xml          |   1 +
 54 files changed, 1266 insertions(+), 1042 deletions(-)
 create mode 100644 app/src/main/java/com/keylesspalace/tusky/entity/Attachment.java
 delete mode 100644 app/src/main/java/com/keylesspalace/tusky/entity/Media.java
 create mode 100644 app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java
 delete mode 100644 app/src/main/java/com/keylesspalace/tusky/util/CustomTabURLSpan.java
 create mode 100644 app/src/main/java/com/keylesspalace/tusky/util/CustomURLSpan.java
 delete mode 100644 app/src/main/res/drawable/conversation_divider_dark.xml
 create mode 100644 app/src/main/res/drawable/conversation_thread_line_dark.xml
 create mode 100644 app/src/main/res/drawable/conversation_thread_line_light.xml
 delete mode 100644 app/src/main/res/drawable/ic_play_48dp.xml
 create mode 100644 app/src/main/res/drawable/ic_play_indicator_dark.xml
 create mode 100644 app/src/main/res/drawable/ic_play_indicator_light.xml
 create mode 100644 app/src/main/res/drawable/ic_reblog_dark_18dp.xml
 create mode 100644 app/src/main/res/drawable/ic_reblog_light_18dp.xml
 create mode 100644 app/src/main/res/drawable/ic_reply_all_24dp.xml

diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java
index a7756baa..32716179 100644
--- a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java
@@ -312,9 +312,7 @@ public final class AccountActivity extends BaseActivity implements ActionButtonA
             getSupportActionBar().setSubtitle(subtitle);
         }
 
-        boolean useCustomTabs = PreferenceManager.getDefaultSharedPreferences(this)
-                .getBoolean("customTabs", false);
-        LinkHelper.setClickableText(note, account.note, null, useCustomTabs, new LinkListener() {
+        LinkHelper.setClickableText(note, account.note, null, new LinkListener() {
             @Override
             public void onViewTag(String tag) {
                 Intent intent = new Intent(AccountActivity.this, ViewTagActivity.class);
diff --git a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java
index c27b2f97..1ac3d196 100644
--- a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java
@@ -78,7 +78,7 @@ import com.keylesspalace.tusky.adapter.MentionAutoCompleteAdapter;
 import com.keylesspalace.tusky.db.TootDao;
 import com.keylesspalace.tusky.db.TootEntity;
 import com.keylesspalace.tusky.entity.Account;
-import com.keylesspalace.tusky.entity.Media;
+import com.keylesspalace.tusky.entity.Attachment;
 import com.keylesspalace.tusky.entity.Status;
 import com.keylesspalace.tusky.fragment.ComposeOptionsFragment;
 import com.keylesspalace.tusky.network.ProgressRequestBody;
@@ -1274,9 +1274,9 @@ public final class ComposeActivity extends BaseActivity
 
         item.uploadRequest = mastodonApi.uploadMedia(body);
 
-        item.uploadRequest.enqueue(new Callback<Media>() {
+        item.uploadRequest.enqueue(new Callback<Attachment>() {
             @Override
-            public void onResponse(@NonNull Call<Media> call, @NonNull retrofit2.Response<Media> response) {
+            public void onResponse(@NonNull Call<Attachment> call, @NonNull retrofit2.Response<Attachment> response) {
                 if (response.isSuccessful()) {
                     onUploadSuccess(item, response.body());
                 } else {
@@ -1286,14 +1286,14 @@ public final class ComposeActivity extends BaseActivity
             }
 
             @Override
-            public void onFailure(@NonNull Call<Media> call, @NonNull Throwable t) {
+            public void onFailure(@NonNull Call<Attachment> call, @NonNull Throwable t) {
                 Log.d(TAG, "Upload request failed. " + t.getMessage());
                 onUploadFailure(item, call.isCanceled());
             }
         });
     }
 
-    private void onUploadSuccess(final QueuedMedia item, Media media) {
+    private void onUploadSuccess(final QueuedMedia item, Attachment media) {
         item.id = media.id;
         item.preview.setProgress(-1);
         item.readyStage = QueuedMedia.ReadyStage.UPLOADED;
@@ -1460,7 +1460,7 @@ public final class ComposeActivity extends BaseActivity
         ProgressImageView preview;
         Uri uri;
         String id;
-        Call<Media> uploadRequest;
+        Call<Attachment> uploadRequest;
         URLSpan uploadUrl;
         ReadyStage readyStage;
         byte[] content;
diff --git a/app/src/main/java/com/keylesspalace/tusky/SearchActivity.java b/app/src/main/java/com/keylesspalace/tusky/SearchActivity.java
index 112189fe..945aa46f 100644
--- a/app/src/main/java/com/keylesspalace/tusky/SearchActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/SearchActivity.java
@@ -20,6 +20,7 @@ import android.app.SearchableInfo;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v7.app.ActionBar;
 import android.support.v7.widget.LinearLayoutManager;
@@ -150,8 +151,7 @@ public class SearchActivity extends BaseActivity implements SearchView.OnQueryTe
         }
 
         searchView.setOnQueryTextListener(this);
-        searchView.setFocusable(false);
-        searchView.setFocusableInTouchMode(false);
+        searchView.requestFocus();
 
         searchView.setMaxWidth(Integer.MAX_VALUE);
     }
@@ -160,7 +160,7 @@ public class SearchActivity extends BaseActivity implements SearchView.OnQueryTe
         clearResults();
         Callback<SearchResults> callback = new Callback<SearchResults>() {
             @Override
-            public void onResponse(Call<SearchResults> call, Response<SearchResults> response) {
+            public void onResponse(@NonNull Call<SearchResults> call, @NonNull Response<SearchResults> response) {
                 if (response.isSuccessful()) {
                     SearchResults results = response.body();
                     if (results.accounts != null && results.accounts.length > 0 || results.hashtags != null && results.hashtags.length > 0) {
@@ -175,7 +175,7 @@ public class SearchActivity extends BaseActivity implements SearchView.OnQueryTe
             }
 
             @Override
-            public void onFailure(Call<SearchResults> call, Throwable t) {
+            public void onFailure(@NonNull Call<SearchResults> call, @NonNull Throwable t) {
                 onSearchFailure();
             }
         };
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
index 4a7dce80..f116473f 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
@@ -19,6 +19,7 @@ import android.content.Context;
 import android.graphics.Color;
 import android.graphics.PorterDuff;
 import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
 import android.support.annotation.Nullable;
 import android.support.v4.content.ContextCompat;
 import android.support.v7.widget.RecyclerView;
@@ -26,7 +27,6 @@ import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.style.StyleSpan;
-import android.text.style.URLSpan;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -38,13 +38,18 @@ import android.widget.ToggleButton;
 import com.keylesspalace.tusky.R;
 import com.keylesspalace.tusky.entity.Notification;
 import com.keylesspalace.tusky.entity.Status;
+import com.keylesspalace.tusky.interfaces.LinkListener;
 import com.keylesspalace.tusky.interfaces.StatusActionListener;
+import com.keylesspalace.tusky.util.CustomEmojiHelper;
+import com.keylesspalace.tusky.util.DateUtils;
+import com.keylesspalace.tusky.util.LinkHelper;
 import com.keylesspalace.tusky.view.RoundedTransformation;
 import com.keylesspalace.tusky.viewdata.NotificationViewData;
 import com.keylesspalace.tusky.viewdata.StatusViewData;
 import com.squareup.picasso.Picasso;
 
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 
 public class NotificationsAdapter extends RecyclerView.Adapter {
@@ -126,13 +131,17 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
                 case FAVOURITE:
                 case REBLOG: {
                     StatusNotificationViewHolder holder = (StatusNotificationViewHolder) viewHolder;
-                    holder.setMessage(type, concreteNotificaton.getAccount().getDisplayName(),
-                            concreteNotificaton.getStatusViewData());
+                    StatusViewData.Concrete statusViewData = concreteNotificaton.getStatusViewData();
+                    holder.setDisplayName(statusViewData.getUserFullName());
+                    holder.setUsername(statusViewData.getNickname());
+                    holder.setCreatedAt(statusViewData.getCreatedAt());
+
+                    holder.setMessage(concreteNotificaton, statusListener);
                     holder.setupButtons(notificationActionListener,
                             concreteNotificaton.getAccount().id,
                             concreteNotificaton.getId());
                     holder.setAvatars(concreteNotificaton.getStatusViewData().getAvatar(),
-                            concreteNotificaton.getId());
+                            concreteNotificaton.getAccount().avatar);
                     break;
                 }
                 case FOLLOW: {
@@ -220,6 +229,9 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
         void onViewAccount(String id);
 
         void onViewStatusForNotificationId(String notificationId);
+
+        void onExpandedChange(boolean expanded, int position);
+
     }
 
     private static class FollowViewHolder extends RecyclerView.ViewHolder {
@@ -270,30 +282,32 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
     private static class StatusNotificationViewHolder extends RecyclerView.ViewHolder
             implements View.OnClickListener, ToggleButton.OnCheckedChangeListener {
         private final TextView message;
-        private final ImageView icon;
+        private final TextView displayName;
+        private final TextView username;
+        private final TextView timestampInfo;
         private final TextView statusContent;
         private final ViewGroup container;
         private final ImageView statusAvatar;
         private final ImageView notificationAvatar;
-        private final ViewGroup topBar;
         private final View contentWarningBar;
         private final TextView contentWarningDescriptionTextView;
         private final ToggleButton contentWarningButton;
 
         private String accountId;
         private String notificationId;
-        private NotificationActionListener listener;
+        private NotificationActionListener notificationActionListener;
         private StatusViewData.Concrete statusViewData;
 
         StatusNotificationViewHolder(View itemView) {
             super(itemView);
-            message = itemView.findViewById(R.id.notification_text);
-            icon = itemView.findViewById(R.id.notification_icon);
+            message = itemView.findViewById(R.id.notification_top_text);
+            displayName = itemView.findViewById(R.id.status_display_name);
+            username = itemView.findViewById(R.id.status_username);
+            timestampInfo = itemView.findViewById(R.id.status_timestamp_info);
             statusContent = itemView.findViewById(R.id.notification_content);
             container = itemView.findViewById(R.id.notification_container);
             statusAvatar = itemView.findViewById(R.id.notification_status_avatar);
             notificationAvatar = itemView.findViewById(R.id.notification_notification_avatar);
-            topBar = itemView.findViewById(R.id.notification_top_bar);
             contentWarningBar = itemView.findViewById(R.id.notification_content_warning_bar);
             contentWarningDescriptionTextView = itemView.findViewById(R.id.notification_content_warning_description);
             contentWarningButton = itemView.findViewById(R.id.notification_content_warning_button);
@@ -303,33 +317,77 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
             notificationAvatar.setColorFilter(darkerFilter, PorterDuff.Mode.MULTIPLY);
 
             container.setOnClickListener(this);
-            topBar.setOnClickListener(this);
+            message.setOnClickListener(this);
+            statusContent.setOnClickListener(this);
             contentWarningButton.setOnCheckedChangeListener(this);
         }
 
-        void setMessage(Notification.Type type, String displayName,
-                        StatusViewData.Concrete status) {
-            this.statusViewData = status;
+        private void setDisplayName(String name) {
+            displayName.setText(name);
+        }
+
+        private void setUsername(String name) {
+            Context context = username.getContext();
+            String format = context.getString(R.string.status_username_format);
+            String usernameText = String.format(format, name);
+            username.setText(usernameText);
+        }
+
+        private void setCreatedAt(@Nullable Date createdAt) {
+            // This is the visible timestampInfo.
+            String readout;
+        /* This one is for screen-readers. Frequently, they would mispronounce timestamps like "17m"
+         * as 17 meters instead of minutes. */
+            CharSequence readoutAloud;
+            if (createdAt != null) {
+                long then = createdAt.getTime();
+                long now = new Date().getTime();
+                readout = DateUtils.getRelativeTimeSpanString(timestampInfo.getContext(), then, now);
+                readoutAloud = android.text.format.DateUtils.getRelativeTimeSpanString(then, now,
+                        android.text.format.DateUtils.SECOND_IN_MILLIS,
+                        android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE);
+            } else {
+                // unknown minutes~
+                readout = "?m";
+                readoutAloud = "? minutes";
+            }
+            timestampInfo.setText(readout);
+            timestampInfo.setContentDescription(readoutAloud);
+        }
+
+        void setMessage(NotificationViewData.Concrete notificationViewData, LinkListener listener) {
+            this.statusViewData = notificationViewData.getStatusViewData();
+
+            String displayName = notificationViewData.getAccount().getDisplayName();
+            Notification.Type type = notificationViewData.getType();
 
             Context context = message.getContext();
             String format;
+            Drawable icon;
             switch (type) {
                 default:
                 case FAVOURITE: {
-                    icon.setImageResource(R.drawable.ic_star_24dp);
-                    icon.setColorFilter(ContextCompat.getColor(context,
-                            R.color.status_favourite_button_marked_dark));
+                    icon = ContextCompat.getDrawable(context, R.drawable.ic_star_24dp);
+                    if (icon != null) {
+                        icon.setColorFilter(ContextCompat.getColor(context,
+                                R.color.status_favourite_button_marked_dark), PorterDuff.Mode.SRC_ATOP);
+                    }
+
                     format = context.getString(R.string.notification_favourite_format);
                     break;
                 }
                 case REBLOG: {
-                    icon.setImageResource(R.drawable.ic_repeat_24dp);
-                    icon.setColorFilter(ContextCompat.getColor(context,
-                            R.color.color_accent_dark));
+                    icon = ContextCompat.getDrawable(context, R.drawable.ic_repeat_24dp);
+                    if(icon != null) {
+                        icon.setColorFilter(ContextCompat.getColor(context,
+                                R.color.color_accent_dark), PorterDuff.Mode.SRC_ATOP);
+                    }
+
                     format = context.getString(R.string.notification_reblog_format);
                     break;
                 }
             }
+            message.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
             String wholeMessage = String.format(format, displayName);
             final SpannableStringBuilder str = new SpannableStringBuilder(wholeMessage);
             str.setSpan(new StyleSpan(Typeface.BOLD), 0, displayName.length(),
@@ -338,12 +396,12 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
 
             boolean hasSpoiler = !TextUtils.isEmpty(statusViewData.getSpoilerText());
             contentWarningBar.setVisibility(hasSpoiler ? View.VISIBLE : View.GONE);
-            setupContentAndSpoiler(false);
+            setupContentAndSpoiler(notificationViewData, listener);
         }
 
         void setupButtons(final NotificationActionListener listener, final String accountId,
                           final String notificationId) {
-            this.listener = listener;
+            this.notificationActionListener = listener;
             this.accountId = accountId;
             this.notificationId = notificationId;
         }
@@ -362,11 +420,11 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
             }
 
             if (notificationAvatarUrl == null || notificationAvatarUrl.isEmpty()) {
-                notificationAvatar.setVisibility(View.GONE);
+                notificationAvatar.setImageResource(R.drawable.avatar_default);
             } else {
-                notificationAvatar.setVisibility(View.VISIBLE);
                 Picasso.with(context)
                         .load(notificationAvatarUrl)
+                        .placeholder(R.drawable.avatar_default)
                         .fit()
                         .transform(new RoundedTransformation(7, 0))
                         .into(notificationAvatar);
@@ -377,46 +435,48 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
         public void onClick(View v) {
             switch (v.getId()) {
                 case R.id.notification_container:
-                    if (listener != null) listener.onViewStatusForNotificationId(notificationId);
+                case R.id.notification_content:
+                    if (notificationActionListener != null) notificationActionListener.onViewStatusForNotificationId(notificationId);
                     break;
-                case R.id.notification_top_bar:
-                    if (listener != null) listener.onViewAccount(accountId);
+                case R.id.notification_top_text:
+                    if (notificationActionListener != null) notificationActionListener.onViewAccount(accountId);
                     break;
             }
         }
 
-        private void setupContentAndSpoiler(boolean shouldShowContentIfSpoiler) {
+        private void setupContentAndSpoiler(NotificationViewData.Concrete notificationViewData, final LinkListener listener) {
+
+            boolean shouldShowContentIfSpoiler = notificationViewData.isExpanded();
             boolean hasSpoiler = !TextUtils.isEmpty(statusViewData.getSpoilerText());
-            CharSequence content;
             if (!shouldShowContentIfSpoiler && hasSpoiler) {
-                if (statusViewData.getMentions() != null &&
-                        statusViewData.getMentions().length > 0) {
-                    // If there is a content warning and mentions we're alternating between
-                    // showing mentions and showing full content. As mentions are plain text we
-                    // have to construct URLSpans ourselves.
-                    SpannableStringBuilder contentBuilder = new SpannableStringBuilder();
-                    for (Status.Mention mention : statusViewData.getMentions()) {
-                        int start = contentBuilder.length() > 0 ? contentBuilder.length() - 1 : 0;
-                        contentBuilder.append('@');
-                        contentBuilder.append(mention.username);
-                        contentBuilder.append(' ');
-                        contentBuilder.setSpan(new URLSpan(mention.url), start,
-                                mention.username.length() + 1, 0);
-                    }
-                    content = contentBuilder;
-                } else {
-                    content = null;
-                }
+                statusContent.setVisibility(View.GONE);
             } else {
-                content = statusViewData.getContent();
+                statusContent.setVisibility(View.VISIBLE);
             }
-            statusContent.setText(content);
-            contentWarningDescriptionTextView.setText(statusViewData.getSpoilerText());
+
+            Spanned content = statusViewData.getContent();
+            List<Status.Emoji> emojis = statusViewData.getEmojis();
+
+            Spanned emojifiedText = CustomEmojiHelper.emojifyText(content, emojis, statusContent);
+
+            LinkHelper.setClickableText(statusContent, emojifiedText, statusViewData.getMentions(), listener);
+
+
+            Spanned emojifiedContentWarning =
+                    CustomEmojiHelper.emojifyString(statusViewData.getSpoilerText(), statusViewData.getEmojis(), contentWarningDescriptionTextView);
+            contentWarningDescriptionTextView.setText(emojifiedContentWarning);
         }
 
         @Override
         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-            setupContentAndSpoiler(isChecked);
+            if (getAdapterPosition() != RecyclerView.NO_POSITION) {
+                notificationActionListener.onExpandedChange(isChecked, getAdapterPosition());
+            }
+            if (isChecked) {
+                statusContent.setVisibility(View.VISIBLE);
+            } else {
+                statusContent.setVisibility(View.GONE);
+            }
         }
     }
 }
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
index 2bb0594b..e45f5cbd 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
@@ -1,21 +1,14 @@
 package com.keylesspalace.tusky.adapter;
 
 import android.content.Context;
-import android.content.SharedPreferences;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
-import android.preference.PreferenceManager;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v7.content.res.AppCompatResources;
 import android.support.v7.widget.RecyclerView;
-import android.text.SpannableStringBuilder;
 import android.text.Spanned;
-import android.text.style.ReplacementSpan;
+import android.text.TextUtils;
 import android.view.View;
 import android.widget.CompoundButton;
 import android.widget.ImageButton;
@@ -24,26 +17,24 @@ import android.widget.TextView;
 import android.widget.ToggleButton;
 
 import com.keylesspalace.tusky.R;
+import com.keylesspalace.tusky.entity.Attachment;
 import com.keylesspalace.tusky.entity.Status;
 import com.keylesspalace.tusky.interfaces.StatusActionListener;
+import com.keylesspalace.tusky.util.CustomEmojiHelper;
 import com.keylesspalace.tusky.util.DateUtils;
+import com.keylesspalace.tusky.util.HtmlUtils;
 import com.keylesspalace.tusky.util.LinkHelper;
 import com.keylesspalace.tusky.util.ThemeUtils;
 import com.keylesspalace.tusky.view.RoundedTransformation;
 import com.keylesspalace.tusky.viewdata.StatusViewData;
-import com.squareup.picasso.Callback;
 import com.squareup.picasso.Picasso;
-import com.squareup.picasso.Target;
 import com.varunest.sparkbutton.SparkButton;
 import com.varunest.sparkbutton.SparkEventListener;
 
-import java.lang.ref.WeakReference;
 import java.util.Date;
 import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
-class StatusBaseViewHolder extends RecyclerView.ViewHolder {
+abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
     private View container;
     private TextView displayName;
     private TextView username;
@@ -58,23 +49,26 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
     private ImageView mediaPreview1;
     private ImageView mediaPreview2;
     private ImageView mediaPreview3;
-    private View sensitiveMediaWarning;
+    private ImageView mediaOverlay0;
+    private ImageView mediaOverlay1;
+    private ImageView mediaOverlay2;
+    private ImageView mediaOverlay3;
+    private TextView sensitiveMediaWarning;
     private View sensitiveMediaShow;
-    private View videoIndicator;
     private TextView mediaLabel;
     private View contentWarningBar;
     private TextView contentWarningDescription;
     private ToggleButton contentWarningButton;
 
     ImageView avatar;
-    TextView timestamp;
+    TextView timestampInfo;
 
     StatusBaseViewHolder(View itemView) {
         super(itemView);
         container = itemView.findViewById(R.id.status_container);
         displayName = itemView.findViewById(R.id.status_display_name);
         username = itemView.findViewById(R.id.status_username);
-        timestamp = itemView.findViewById(R.id.status_timestamp);
+        timestampInfo = itemView.findViewById(R.id.status_timestamp_info);
         content = itemView.findViewById(R.id.status_content);
         avatar = itemView.findViewById(R.id.status_avatar);
         replyButton = itemView.findViewById(R.id.status_reply);
@@ -87,15 +81,20 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
         mediaPreview1 = itemView.findViewById(R.id.status_media_preview_1);
         mediaPreview2 = itemView.findViewById(R.id.status_media_preview_2);
         mediaPreview3 = itemView.findViewById(R.id.status_media_preview_3);
+        mediaOverlay0 = itemView.findViewById(R.id.status_media_overlay_0);
+        mediaOverlay1 = itemView.findViewById(R.id.status_media_overlay_1);
+        mediaOverlay2 = itemView.findViewById(R.id.status_media_overlay_2);
+        mediaOverlay3 = itemView.findViewById(R.id.status_media_overlay_3);
         sensitiveMediaWarning = itemView.findViewById(R.id.status_sensitive_media_warning);
         sensitiveMediaShow = itemView.findViewById(R.id.status_sensitive_media_button);
-        videoIndicator = itemView.findViewById(R.id.status_video_indicator);
         mediaLabel = itemView.findViewById(R.id.status_media_label);
         contentWarningBar = itemView.findViewById(R.id.status_content_warning_bar);
         contentWarningDescription = itemView.findViewById(R.id.status_content_warning_description);
         contentWarningButton = itemView.findViewById(R.id.status_content_warning_button);
     }
 
+    protected abstract int getMediaPreviewHeight(Context context);
+
     private void setDisplayName(String name) {
         displayName.setText(name);
     }
@@ -107,44 +106,11 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
         username.setText(usernameText);
     }
 
-    private Callback spanCallback = new Callback() {
-        @Override
-        public void onSuccess() {
-            content.invalidate();
-        }
-
-        @Override
-        public void onError() {
-        }
-    };
-
     private void setContent(Spanned content, Status.Mention[] mentions, List<Status.Emoji> emojis,
                             StatusActionListener listener) {
-        Context context = this.content.getContext();
-        SpannableStringBuilder builder = new SpannableStringBuilder(content);
-        if (!emojis.isEmpty()) {
-            CharSequence text = builder.subSequence(0, builder.length());
-            for (Status.Emoji emoji : emojis) {
-                CharSequence pattern = new StringBuilder(":").append(emoji.getShortcode()).append(':');
-                Matcher matcher = Pattern.compile(pattern.toString()).matcher(text);
-                while (matcher.find()) {
-                    // We keep a span as a Picasso target, because Picasso keeps weak reference to
-                    // the target so an anonymous class would likely be garbage collected.
-                    EmojiSpan span = new EmojiSpan(context);
-                    span.setCallback(spanCallback);
-                    builder.setSpan(span, matcher.start(), matcher.end(), 0);
-                    Picasso.with(container.getContext())
-                            .load(emoji.getUrl())
-                            .into(span);
-                }
-            }
-        }
+        Spanned emojifiedText = CustomEmojiHelper.emojifyText(content, emojis, this.content);
 
-        /* Redirect URLSpan's in the status content to the listener for viewing tag pages and
-         * account pages. */
-        boolean useCustomTabs =
-                PreferenceManager.getDefaultSharedPreferences(context).getBoolean("customTabs", false);
-        LinkHelper.setClickableText(this.content, builder, mentions, useCustomTabs, listener);
+        LinkHelper.setClickableText(this.content, emojifiedText, mentions, listener);
     }
 
     void setAvatar(String url, @Nullable String rebloggedUrl) {
@@ -160,7 +126,7 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
     }
 
     protected void setCreatedAt(@Nullable Date createdAt) {
-        // This is the visible timestamp.
+        // This is the visible timestampInfo.
         String readout;
         /* This one is for screen-readers. Frequently, they would mispronounce timestamps like "17m"
          * as 17 meters instead of minutes. */
@@ -168,7 +134,7 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
         if (createdAt != null) {
             long then = createdAt.getTime();
             long now = new Date().getTime();
-            readout = DateUtils.getRelativeTimeSpanString(timestamp.getContext(), then, now);
+            readout = DateUtils.getRelativeTimeSpanString(timestampInfo.getContext(), then, now);
             readoutAloud = android.text.format.DateUtils.getRelativeTimeSpanString(then, now,
                     android.text.format.DateUtils.SECOND_IN_MILLIS,
                     android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE);
@@ -177,8 +143,18 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
             readout = "?m";
             readoutAloud = "? minutes";
         }
-        timestamp.setText(readout);
-        timestamp.setContentDescription(readoutAloud);
+        timestampInfo.setText(readout);
+        timestampInfo.setContentDescription(readoutAloud);
+    }
+
+
+    private void setIsReply(boolean isReply) {
+        if(isReply) {
+            replyButton.setImageResource(R.drawable.ic_reply_all_24dp);
+        } else {
+            replyButton.setImageResource(R.drawable.ic_reply_24dp);
+        }
+
     }
 
     private void setReblogged(boolean reblogged) {
@@ -214,11 +190,14 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
         favouriteButton.setChecked(favourited);
     }
 
-    private void setMediaPreviews(final Status.MediaAttachment[] attachments, boolean sensitive,
-                                  final StatusActionListener listener, boolean showingSensitive) {
+    private void setMediaPreviews(final Attachment[] attachments, boolean sensitive,
+                                  final StatusActionListener listener, boolean showingContent) {
         final ImageView[] previews = {
                 mediaPreview0, mediaPreview1, mediaPreview2, mediaPreview3
         };
+        final ImageView[] overlays = {
+                mediaOverlay0, mediaOverlay1, mediaOverlay2, mediaOverlay3
+        };
         Context context = mediaPreview0.getContext();
 
         int mediaPreviewUnloadedId =
@@ -234,6 +213,13 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
 
         for (int i = 0; i < n; i++) {
             String previewUrl = attachments[i].previewUrl;
+            String description = attachments[i].description;
+
+            if(TextUtils.isEmpty(description)) {
+                previews[i].setContentDescription(context.getString(R.string.action_view_media));
+            } else {
+                previews[i].setContentDescription(description);
+            }
 
             previews[i].setVisibility(View.VISIBLE);
 
@@ -246,9 +232,11 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
                         .into(previews[i]);
             }
 
-            final Status.MediaAttachment.Type type = attachments[i].type;
-            if (type == Status.MediaAttachment.Type.VIDEO | type == Status.MediaAttachment.Type.GIFV) {
-                videoIndicator.setVisibility(View.VISIBLE);
+            final Attachment.Type type = attachments[i].type;
+            if (type == Attachment.Type.VIDEO | type == Attachment.Type.GIFV) {
+                overlays[i].setVisibility(View.VISIBLE);
+            } else {
+                overlays[i].setVisibility(View.GONE);
             }
 
             if (urls[i] == null || urls[i].isEmpty()) {
@@ -262,34 +250,56 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
                     }
                 });
             }
+
+            if(n <= 2) {
+                previews[0].getLayoutParams().height = getMediaPreviewHeight(context)*2;
+                previews[1].getLayoutParams().height = getMediaPreviewHeight(context)*2;
+            } else {
+                previews[0].getLayoutParams().height = getMediaPreviewHeight(context);
+                previews[1].getLayoutParams().height = getMediaPreviewHeight(context);
+                previews[2].getLayoutParams().height = getMediaPreviewHeight(context);
+                previews[3].getLayoutParams().height = getMediaPreviewHeight(context);
+            }
         }
-        SharedPreferences pm = PreferenceManager.getDefaultSharedPreferences(context);
-        Boolean isAlwayShowSensitive = pm.getBoolean("alwaysShowSensitiveMedia", false);
-        if (sensitive && (!isAlwayShowSensitive)) {
-            sensitiveMediaWarning.setVisibility(showingSensitive ? View.GONE : View.VISIBLE);
-            sensitiveMediaShow.setVisibility(showingSensitive ? View.VISIBLE : View.GONE);
-            sensitiveMediaShow.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    if (getAdapterPosition() != RecyclerView.NO_POSITION) {
-                        listener.onContentHiddenChange(false, getAdapterPosition());
-                    }
-                    v.setVisibility(View.GONE);
-                    sensitiveMediaWarning.setVisibility(View.VISIBLE);
-                }
-            });
-            sensitiveMediaWarning.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    if (getAdapterPosition() != RecyclerView.NO_POSITION) {
-                        listener.onContentHiddenChange(true, getAdapterPosition());
-                    }
-                    v.setVisibility(View.GONE);
-                    sensitiveMediaShow.setVisibility(View.VISIBLE);
-                }
-            });
+
+        String hiddenContentText;
+        if(sensitive) {
+            hiddenContentText = context.getString(R.string.status_sensitive_media_template,
+                    context.getString(R.string.status_sensitive_media_title),
+                    context.getString(R.string.status_sensitive_media_directions));
+
+        } else {
+            hiddenContentText = context.getString(R.string.status_sensitive_media_template,
+                    context.getString(R.string.status_media_hidden_title),
+                    context.getString(R.string.status_sensitive_media_directions));
         }
 
+        sensitiveMediaWarning.setText(HtmlUtils.fromHtml(hiddenContentText));
+
+        sensitiveMediaWarning.setVisibility(showingContent ? View.GONE : View.VISIBLE);
+        sensitiveMediaShow.setVisibility(showingContent ? View.VISIBLE : View.GONE);
+        sensitiveMediaShow.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (getAdapterPosition() != RecyclerView.NO_POSITION) {
+                    listener.onContentHiddenChange(false, getAdapterPosition());
+                }
+                v.setVisibility(View.GONE);
+                sensitiveMediaWarning.setVisibility(View.VISIBLE);
+            }
+        });
+        sensitiveMediaWarning.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (getAdapterPosition() != RecyclerView.NO_POSITION) {
+                    listener.onContentHiddenChange(true, getAdapterPosition());
+                }
+                v.setVisibility(View.GONE);
+                sensitiveMediaShow.setVisibility(View.VISIBLE);
+            }
+        });
+
+
         // Hide any of the placeholder previews beyond the ones set.
         for (int i = n; i < Status.MAX_MEDIA_ATTACHMENTS; i++) {
             previews[i].setVisibility(View.GONE);
@@ -297,7 +307,7 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
     }
 
     @NonNull
-    private static String getLabelTypeText(Context context, Status.MediaAttachment.Type type) {
+    private static String getLabelTypeText(Context context, Attachment.Type type) {
         switch (type) {
             default:
             case IMAGE:
@@ -309,7 +319,7 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
     }
 
     @DrawableRes
-    private static int getLabelIcon(Status.MediaAttachment.Type type) {
+    private static int getLabelIcon(Attachment.Type type) {
         switch (type) {
             default:
             case IMAGE:
@@ -320,7 +330,7 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
         }
     }
 
-    private void setMediaLabel(Status.MediaAttachment[] attachments, boolean sensitive,
+    private void setMediaLabel(Attachment[] attachments, boolean sensitive,
                                final StatusActionListener listener) {
         if (attachments.length == 0) {
             mediaLabel.setVisibility(View.GONE);
@@ -349,7 +359,7 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
         for (int i = 0; i < n; i++) {
             urls[i] = attachments[i].url;
         }
-        final Status.MediaAttachment.Type type = attachments[0].type;
+        final Attachment.Type type = attachments[0].type;
         mediaLabel.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -363,14 +373,17 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
         sensitiveMediaShow.setVisibility(View.GONE);
     }
 
-    private void setSpoilerText(String spoilerText, final boolean expanded,
-                                final StatusActionListener listener) {
-        contentWarningDescription.setText(spoilerText);
+    private void setSpoilerText(String spoilerText, List<Status.Emoji> emojis,
+                                final boolean expanded, final StatusActionListener listener) {
+        CharSequence emojiSpoiler =
+                CustomEmojiHelper.emojifyString(spoilerText, emojis, contentWarningDescription);
+        contentWarningDescription.setText(emojiSpoiler);
         contentWarningBar.setVisibility(View.VISIBLE);
         contentWarningButton.setChecked(expanded);
         contentWarningButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                contentWarningDescription.invalidate();
                 if (getAdapterPosition() != RecyclerView.NO_POSITION) {
                     listener.onExpandedChange(isChecked, getAdapterPosition());
                 }
@@ -478,21 +491,19 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
         setDisplayName(status.getUserFullName());
         setUsername(status.getNickname());
         setCreatedAt(status.getCreatedAt());
+        setIsReply(status.getInReplyToId() != null);
         setContent(status.getContent(), status.getMentions(), status.getEmojis(), listener);
         setAvatar(status.getAvatar(), status.getRebloggedAvatar());
         setReblogged(status.isReblogged());
         setFavourited(status.isFavourited());
-        Status.MediaAttachment[] attachments = status.getAttachments();
+        Attachment[] attachments = status.getAttachments();
         boolean sensitive = status.isSensitive();
         if (mediaPreviewEnabled) {
-            setMediaPreviews(attachments, sensitive, listener, status.isShowingSensitiveContent());
-            /* A status without attachments is sometimes still marked sensitive, so it's necessary
-             * to check both whether there are any attachments and if it's marked sensitive. */
-            if (!sensitive || attachments.length == 0) {
-                hideSensitiveMediaWarning();
-            }
+            setMediaPreviews(attachments, sensitive, listener, status.isShowingContent());
+
             if (attachments.length == 0) {
-                videoIndicator.setVisibility(View.GONE);
+                hideSensitiveMediaWarning();
+//                videoIndicator.setVisibility(View.GONE);
             }
             // Hide the unused label.
             mediaLabel.setVisibility(View.GONE);
@@ -504,7 +515,7 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
             mediaPreview2.setVisibility(View.GONE);
             mediaPreview3.setVisibility(View.GONE);
             hideSensitiveMediaWarning();
-            videoIndicator.setVisibility(View.GONE);
+//            videoIndicator.setVisibility(View.GONE);
         }
 
         setupButtons(listener, status.getSenderId());
@@ -512,66 +523,9 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
         if (status.getSpoilerText() == null || status.getSpoilerText().isEmpty()) {
             hideSpoilerText();
         } else {
-            setSpoilerText(status.getSpoilerText(), status.isExpanded(), listener);
+            setSpoilerText(status.getSpoilerText(), status.getEmojis(), status.isExpanded(), listener);
         }
     }
 
-    private static class EmojiSpan extends ReplacementSpan implements Target {
 
-        private @Nullable
-        Drawable imageDrawable;
-        private WeakReference<Callback> callbackWeakReference;
-        private Context context;
-
-        EmojiSpan(Context context) {
-            this.context = context.getApplicationContext();
-        }
-
-        public void setCallback(Callback callback) {
-            this.callbackWeakReference = new WeakReference<>(callback);
-        }
-
-        @Override
-        public int getSize(@NonNull Paint paint, CharSequence text, int start, int end,
-                           @Nullable Paint.FontMetricsInt fm) {
-            return (int) (paint.getTextSize()*1.2);
-        }
-
-        @Override
-        public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x,
-                         int top, int y, int bottom, @NonNull Paint paint) {
-            if (imageDrawable == null) return;
-            canvas.save();
-
-            int emojiSize = (int) (paint.getTextSize() * 1.1);
-            imageDrawable.setBounds(0, 0, emojiSize, emojiSize);
-
-            int transY = bottom - imageDrawable.getBounds().bottom;
-            transY -= paint.getFontMetricsInt().descent/2;
-            canvas.translate(x, transY);
-            imageDrawable.draw(canvas);
-            canvas.restore();
-        }
-
-        @Override
-        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
-            // I hope using resources from application context is okay
-            // It's probably better than keeping activity alive. My assumption is that resources are
-            // only needed to look up the density which is really unlikely to change with
-            // configuration
-            imageDrawable = new BitmapDrawable(context.getResources(), bitmap);
-            if (callbackWeakReference != null) {
-                Callback cb = callbackWeakReference.get();
-                if (cb != null) cb.onSuccess();
-            }
-        }
-
-        @Override
-        public void onBitmapFailed(Drawable errorDrawable) {
-        }
-
-        @Override
-        public void onPrepareLoad(Drawable placeHolderDrawable) {
-        }
-    }
 }
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java
index 8461d776..d31b9d76 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java
@@ -2,7 +2,6 @@ package com.keylesspalace.tusky.adapter;
 
 import android.content.Context;
 import android.os.Build;
-import android.preference.PreferenceManager;
 import android.support.annotation.Nullable;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
@@ -19,7 +18,7 @@ import com.keylesspalace.tusky.R;
 import com.keylesspalace.tusky.entity.Card;
 import com.keylesspalace.tusky.entity.Status;
 import com.keylesspalace.tusky.interfaces.StatusActionListener;
-import com.keylesspalace.tusky.util.CustomTabURLSpan;
+import com.keylesspalace.tusky.util.CustomURLSpan;
 import com.keylesspalace.tusky.util.LinkHelper;
 import com.keylesspalace.tusky.viewdata.StatusViewData;
 import com.squareup.picasso.Picasso;
@@ -30,7 +29,6 @@ import java.util.Date;
 class StatusDetailedViewHolder extends StatusBaseViewHolder {
     private TextView reblogs;
     private TextView favourites;
-    private TextView application;
     private LinearLayout cardView;
     private LinearLayout cardInfo;
     private ImageView cardImage;
@@ -42,7 +40,6 @@ class StatusDetailedViewHolder extends StatusBaseViewHolder {
         super(view);
         reblogs = view.findViewById(R.id.status_reblogs);
         favourites = view.findViewById(R.id.status_favourites);
-        application = view.findViewById(R.id.status_application);
         cardView = view.findViewById(R.id.card_view);
         cardInfo = view.findViewById(R.id.card_info);
         cardImage = view.findViewById(R.id.card_image);
@@ -51,36 +48,36 @@ class StatusDetailedViewHolder extends StatusBaseViewHolder {
         cardUrl = view.findViewById(R.id.card_link);
     }
 
+    @Override
+    protected int getMediaPreviewHeight(Context context) {
+        return context.getResources().getDimensionPixelSize(R.dimen.status_detail_media_preview_height);
+    }
+
     @Override
     protected void setCreatedAt(@Nullable Date createdAt) {
         if (createdAt != null) {
-            DateFormat dateFormat = android.text.format.DateFormat.getMediumDateFormat(
-                    timestamp.getContext());
-            timestamp.setText(dateFormat.format(createdAt));
+            DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.SHORT);
+            timestampInfo.setText(dateFormat.format(createdAt));
         } else {
-            timestamp.setText("");
+            timestampInfo.setText("");
         }
     }
 
     private void setApplication(@Nullable Status.Application app) {
-        if (app == null) {
-            application.setText("");
-        } else if (app.website != null) {
-            URLSpan span;
-            Context context = application.getContext();
-            boolean useCustomTabs = PreferenceManager.getDefaultSharedPreferences(context)
-                    .getBoolean("customTabs", true);
-            if (useCustomTabs) {
-                span = new CustomTabURLSpan(app.website);
+        if (app != null) {
+
+            timestampInfo.append("  •  ");
+
+            if (app.website != null) {
+                URLSpan span = new CustomURLSpan(app.website);
+
+                SpannableStringBuilder text = new SpannableStringBuilder(app.name);
+                text.setSpan(span, 0, app.name.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+                timestampInfo.append(text);
+                timestampInfo.setMovementMethod(LinkMovementMethod.getInstance());
             } else {
-                span = new URLSpan(app.website);
+                timestampInfo.append(app.name);
             }
-            SpannableStringBuilder text = new SpannableStringBuilder(app.name);
-            text.setSpan(span, 0, app.name.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
-            application.setText(text);
-            application.setMovementMethod(LinkMovementMethod.getInstance());
-        } else {
-            application.setText(app.name);
         }
     }
 
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java
index 943e7d72..3e45e001 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java
@@ -31,14 +31,12 @@ import com.varunest.sparkbutton.helpers.Utils;
 
 public class StatusViewHolder extends StatusBaseViewHolder {
     private ImageView avatarReblog;
-    private View rebloggedBar;
-    private TextView rebloggedByDisplayName;
+    private TextView rebloggedBar;
 
     StatusViewHolder(View itemView) {
         super(itemView);
         avatarReblog = itemView.findViewById(R.id.status_avatar_reblog);
-        rebloggedBar = itemView.findViewById(R.id.status_reblogged_bar);
-        rebloggedByDisplayName = itemView.findViewById(R.id.status_reblogged);
+        rebloggedBar = itemView.findViewById(R.id.status_reblogged);
     }
 
     @Override
@@ -66,6 +64,11 @@ public class StatusViewHolder extends StatusBaseViewHolder {
         }
     }
 
+    @Override
+    protected int getMediaPreviewHeight(Context context) {
+        return context.getResources().getDimensionPixelSize(R.dimen.status_media_preview_height);
+    }
+
     @Override
     void setupWithStatus(StatusViewData.Concrete status, final StatusActionListener listener,
                          boolean mediaPreviewEnabled) {
@@ -90,10 +93,10 @@ public class StatusViewHolder extends StatusBaseViewHolder {
     }
 
     private void setRebloggedByDisplayName(String name) {
-        Context context = rebloggedByDisplayName.getContext();
+        Context context = rebloggedBar.getContext();
         String format = context.getString(R.string.status_boosted_format);
         String boostedText = String.format(format, name);
-        rebloggedByDisplayName.setText(boostedText);
+        rebloggedBar.setText(boostedText);
         rebloggedBar.setVisibility(View.VISIBLE);
     }
 
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.java
index c743a54b..63757e8f 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.java
@@ -22,7 +22,6 @@ import android.view.View;
 import android.view.ViewGroup;
 
 import com.keylesspalace.tusky.R;
-import com.keylesspalace.tusky.entity.Card;
 import com.keylesspalace.tusky.interfaces.StatusActionListener;
 import com.keylesspalace.tusky.viewdata.StatusViewData;
 
@@ -38,8 +37,6 @@ public class ThreadAdapter extends RecyclerView.Adapter {
     private boolean mediaPreviewEnabled;
     private int detailedStatusPosition;
 
-    private Card detailedStatusCard;
-
     public ThreadAdapter(StatusActionListener listener) {
         this.statusActionListener = listener;
         this.statuses = new ArrayList<>();
@@ -155,4 +152,8 @@ public class ThreadAdapter extends RecyclerView.Adapter {
             detailedStatusPosition = position;
         }
     }
+
+    public int getDetailedStatusPosition() {
+        return detailedStatusPosition;
+    }
 }
diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Attachment.java b/app/src/main/java/com/keylesspalace/tusky/entity/Attachment.java
new file mode 100644
index 00000000..355f03c2
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/entity/Attachment.java
@@ -0,0 +1,79 @@
+/* Copyright 2017 Andrew Dawson
+ *
+ * This file is a part of Tusky.
+ *
+ * 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.
+ *
+ * Tusky 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 Tusky; if not,
+ * see <http://www.gnu.org/licenses>. */
+
+package com.keylesspalace.tusky.entity;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.annotations.JsonAdapter;
+import com.google.gson.annotations.SerializedName;
+
+public class Attachment {
+    public String id;
+
+    public String url;
+
+    @SerializedName("preview_url")
+    public String previewUrl;
+
+    @SerializedName("text_url")
+    public String textUrl;
+
+    public Type type;
+
+    public String description;
+
+    public static class Meta {
+        public MediaProperties original;
+        public MediaProperties small;
+    }
+
+    public static class MediaProperties {
+        public int width;
+        public int height;
+        public float aspect;
+    }
+
+    @JsonAdapter(MediaTypeDeserializer.class)
+    public enum Type {
+        @SerializedName("image")
+        IMAGE,
+        @SerializedName("gifv")
+        GIFV,
+        @SerializedName("video")
+        VIDEO,
+        @SerializedName("unknown")
+        UNKNOWN
+    }
+
+    static class MediaTypeDeserializer implements JsonDeserializer<Type> {
+        @Override
+        public Type deserialize(JsonElement json, java.lang.reflect.Type classOfT, JsonDeserializationContext context)
+                throws JsonParseException {
+            switch(json.toString()) {
+                case "\"image\"":
+                    return Type.IMAGE;
+                case "\"gifv\"":
+                    return Type.GIFV;
+                case "\"video\"":
+                    return Type.VIDEO;
+                default:
+                    return Type.UNKNOWN;
+            }
+        }
+    }
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Media.java b/app/src/main/java/com/keylesspalace/tusky/entity/Media.java
deleted file mode 100644
index 82760886..00000000
--- a/app/src/main/java/com/keylesspalace/tusky/entity/Media.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Copyright 2017 Andrew Dawson
- *
- * This file is a part of Tusky.
- *
- * 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.
- *
- * Tusky 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 Tusky; if not,
- * see <http://www.gnu.org/licenses>. */
-
-package com.keylesspalace.tusky.entity;
-
-import com.google.gson.annotations.SerializedName;
-
-public class Media {
-    public String id;
-
-    public String type;
-
-    public String url;
-
-    @SerializedName("preview_url")
-    public String previewUrl;
-
-    @SerializedName("text_url")
-    public String textUrl;
-}
diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Status.java b/app/src/main/java/com/keylesspalace/tusky/entity/Status.java
index 7f9771ee..1e7ef472 100644
--- a/app/src/main/java/com/keylesspalace/tusky/entity/Status.java
+++ b/app/src/main/java/com/keylesspalace/tusky/entity/Status.java
@@ -17,10 +17,6 @@ package com.keylesspalace.tusky.entity;
 
 import android.text.Spanned;
 
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
 import com.google.gson.annotations.SerializedName;
 
 import java.util.Date;
@@ -137,7 +133,7 @@ public class Status {
     }
 
     @SerializedName("media_attachments")
-    public MediaAttachment[] attachments;
+    public Attachment[] attachments;
 
     public Mention[] mentions;
 
@@ -159,48 +155,6 @@ public class Status {
         return id != null ? id.hashCode() : 0;
     }
 
-    public static class MediaAttachment {
-        @com.google.gson.annotations.JsonAdapter(MediaTypeDeserializer.class)
-        public enum Type {
-            @SerializedName("image")
-            IMAGE,
-            @SerializedName("gifv")
-            GIFV,
-            @SerializedName("video")
-            VIDEO,
-            UNKNOWN
-        }
-
-        public String url;
-
-        @SerializedName("preview_url")
-        public String previewUrl;
-
-        @SerializedName("text_url")
-        public String textUrl;
-
-        @SerializedName("remote_url")
-        public String remoteUrl;
-
-        public Type type;
-
-        static class MediaTypeDeserializer implements JsonDeserializer<Type> {
-            @Override
-            public Type deserialize(JsonElement json, java.lang.reflect.Type classOfT, JsonDeserializationContext context)
-                    throws JsonParseException {
-                switch(json.toString()) {
-                    case "\"image\"":
-                        return Type.IMAGE;
-                    case "\"gifv\"":
-                        return Type.GIFV;
-                    case "\"video\"":
-                        return Type.VIDEO;
-                    default:
-                        return Type.UNKNOWN;
-                }
-            }
-        }
-    }
 
     public static final class Mention {
         public String id;
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/AccountMediaFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/AccountMediaFragment.kt
index 6e18fa4c..22abcca3 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/AccountMediaFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/AccountMediaFragment.kt
@@ -34,6 +34,7 @@ import com.keylesspalace.tusky.BaseActivity
 import com.keylesspalace.tusky.R
 import com.keylesspalace.tusky.ViewMediaActivity
 import com.keylesspalace.tusky.ViewVideoActivity
+import com.keylesspalace.tusky.entity.Attachment
 import com.keylesspalace.tusky.entity.Status
 import com.keylesspalace.tusky.network.MastodonApi
 import com.keylesspalace.tusky.view.SquareImageView
@@ -86,7 +87,7 @@ class AccountMediaFragment : BaseFragment() {
             body?.let { fetched ->
                 statuses.addAll(0, fetched)
                 // flatMap requires iterable but I don't want to box each array into list
-                val result = mutableListOf<Status.MediaAttachment>()
+                val result = mutableListOf<Attachment>()
                 for (status in fetched) {
                     result.addAll(status.attachments)
                 }
@@ -110,7 +111,7 @@ class AccountMediaFragment : BaseFragment() {
                 statuses.addAll(fetched)
                 Log.d(TAG, "now there are ${statuses.size} statuses")
                 // flatMap requires iterable but I don't want to box each array into list
-                val result = mutableListOf<Status.MediaAttachment>()
+                val result = mutableListOf<Attachment>()
                 for (status in fetched) {
                     result.addAll(status.attachments)
                 }
@@ -190,12 +191,12 @@ class AccountMediaFragment : BaseFragment() {
         }
     }
 
-    private fun viewMedia(items: List<Status.MediaAttachment>, currentIndex: Int, view: View?) {
+    private fun viewMedia(items: List<Attachment>, currentIndex: Int, view: View?) {
         val urls = items.map { it.url }.toTypedArray()
         val type = items[currentIndex].type
 
         when (type) {
-            Status.MediaAttachment.Type.IMAGE -> {
+            Attachment.Type.IMAGE -> {
                 val intent = Intent(context, ViewMediaActivity::class.java)
                 intent.putExtra("urls", urls)
                 intent.putExtra("urlIndex", currentIndex)
@@ -208,12 +209,12 @@ class AccountMediaFragment : BaseFragment() {
                     startActivity(intent)
                 }
             }
-            Status.MediaAttachment.Type.GIFV, Status.MediaAttachment.Type.VIDEO -> {
+            Attachment.Type.GIFV, Attachment.Type.VIDEO -> {
                 val intent = Intent(context, ViewVideoActivity::class.java)
                 intent.putExtra("url", urls[currentIndex])
                 startActivity(intent)
             }
-            Status.MediaAttachment.Type.UNKNOWN, null -> {
+            Attachment.Type.UNKNOWN, null -> {
             }/* Intentionally do nothing. This case is here is to handle when new attachment
                  * types are added to the API before code is added here to handle them. So, the
                  * best fallback is to just show the preview and ignore requests to view them. */
@@ -229,16 +230,16 @@ class AccountMediaFragment : BaseFragment() {
 
         var baseItemColor = Color.BLACK
 
-        private val items = mutableListOf<Status.MediaAttachment>()
+        private val items = mutableListOf<Attachment>()
         private val itemBgBaseHSV = FloatArray(3)
         private val random = Random()
 
-        fun addTop(newItems: List<Status.MediaAttachment>) {
+        fun addTop(newItems: List<Attachment>) {
             items.addAll(0, newItems)
             notifyItemRangeInserted(0, newItems.size)
         }
 
-        fun addBottom(newItems: List<Status.MediaAttachment>) {
+        fun addBottom(newItems: List<Attachment>) {
             if (newItems.isEmpty()) return
 
             val oldLen = items.size
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java
index 1ca8a19a..772467f1 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java
@@ -42,6 +42,7 @@ import com.keylesspalace.tusky.NotificationPullJobCreator;
 import com.keylesspalace.tusky.adapter.FooterViewHolder;
 import com.keylesspalace.tusky.adapter.NotificationsAdapter;
 import com.keylesspalace.tusky.R;
+import com.keylesspalace.tusky.entity.Attachment;
 import com.keylesspalace.tusky.entity.Notification;
 import com.keylesspalace.tusky.entity.Status;
 import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
@@ -108,6 +109,7 @@ public class NotificationsFragment extends SFragment implements
     private int bottomFetches;
     private String bottomId;
     private String topId;
+    private boolean alwaysShowSensitiveMedia;
 
     // Each element is either a Notification for loading data or a Placeholder
     private final PairedList<Either<Placeholder, Notification>, NotificationViewData> notifications
@@ -116,7 +118,7 @@ public class NotificationsFragment extends SFragment implements
         public NotificationViewData apply(Either<Placeholder, Notification> input) {
             if (input.isRight()) {
                 Notification notification = input.getAsRight();
-                return ViewDataUtils.notificationToViewData(notification);
+                return ViewDataUtils.notificationToViewData(notification, alwaysShowSensitiveMedia);
             } else {
                 return new NotificationViewData.Placeholder(false);
             }
@@ -155,6 +157,7 @@ public class NotificationsFragment extends SFragment implements
         adapter = new NotificationsAdapter(this, this);
         SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(
                 getActivity());
+        alwaysShowSensitiveMedia = preferences.getBoolean("alwaysShowSensitiveMedia", false);
         boolean mediaPreviewEnabled = preferences.getBoolean("mediaPreviewEnabled", true);
         adapter.setMediaPreviewEnabled(mediaPreviewEnabled);
         recyclerView.setAdapter(adapter);
@@ -273,13 +276,19 @@ public class NotificationsFragment extends SFragment implements
                     if (status.reblog != null) {
                         status.reblog.reblogged = reblog;
                     }
-                    // Java's type inference *eyeroll*
-                    notifications.set(position,
-                            Either.<Placeholder, Notification>right(notification));
 
-                    adapter.updateItemWithNotify(position, notifications.getPairedItem(position), true);
+                    NotificationViewData.Concrete viewdata = (NotificationViewData.Concrete)notifications.getPairedItem(position);
 
-                    adapter.notifyItemChanged(position);
+                    StatusViewData.Builder viewDataBuilder = new StatusViewData.Builder(viewdata.getStatusViewData());
+                    viewDataBuilder.setReblogged(reblog);
+
+                    NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
+                            viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
+                            viewDataBuilder.createStatusViewData(), viewdata.isExpanded());
+
+                    notifications.setPairedItem(position, newViewData);
+
+                    adapter.updateItemWithNotify(position, newViewData, true);
                 }
             }
 
@@ -305,12 +314,19 @@ public class NotificationsFragment extends SFragment implements
                         status.reblog.favourited = favourite;
                     }
 
-                    notifications.set(position,
-                            Either.<Placeholder, Notification>right(notification));
+                    NotificationViewData.Concrete viewdata = (NotificationViewData.Concrete)notifications.getPairedItem(position);
 
-                    adapter.updateItemWithNotify(position, notifications.getPairedItem(position), true);
+                    StatusViewData.Builder viewDataBuilder = new StatusViewData.Builder(viewdata.getStatusViewData());
+                    viewDataBuilder.setFavourited(favourite);
+
+                    NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
+                            viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
+                            viewDataBuilder.createStatusViewData(), viewdata.isExpanded());
+
+                    notifications.setPairedItem(position, newViewData);
+
+                    adapter.updateItemWithNotify(position, newViewData, true);
 
-                    adapter.notifyItemChanged(position);
                 }
             }
 
@@ -328,7 +344,7 @@ public class NotificationsFragment extends SFragment implements
     }
 
     @Override
-    public void onViewMedia(String[] urls, int urlIndex, Status.MediaAttachment.Type type,
+    public void onViewMedia(String[] urls, int urlIndex, Attachment.Type type,
                             View view) {
         super.viewMedia(urls, urlIndex, type, view);
     }
@@ -354,7 +370,7 @@ public class NotificationsFragment extends SFragment implements
                         .setIsExpanded(expanded)
                         .createStatusViewData();
         NotificationViewData notificationViewData = new NotificationViewData.Concrete(old.getType(),
-                old.getId(), old.getAccount(), statusViewData);
+                old.getId(), old.getAccount(), statusViewData, expanded);
         notifications.setPairedItem(position, notificationViewData);
         adapter.updateItemWithNotify(position, notificationViewData, false);
     }
@@ -368,7 +384,7 @@ public class NotificationsFragment extends SFragment implements
                         .setIsShowingSensitiveContent(isShowing)
                         .createStatusViewData();
         NotificationViewData notificationViewData = new NotificationViewData.Concrete(old.getType(),
-                old.getId(), old.getAccount(), statusViewData);
+                old.getId(), old.getAccount(), statusViewData, old.isExpanded());
         notifications.setPairedItem(position, notificationViewData);
         adapter.updateItemWithNotify(position, notificationViewData, false);
     }
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
index eb20619b..7a9a96ec 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
@@ -40,6 +40,7 @@ import com.keylesspalace.tusky.ViewMediaActivity;
 import com.keylesspalace.tusky.ViewTagActivity;
 import com.keylesspalace.tusky.ViewThreadActivity;
 import com.keylesspalace.tusky.ViewVideoActivity;
+import com.keylesspalace.tusky.entity.Attachment;
 import com.keylesspalace.tusky.entity.Relationship;
 import com.keylesspalace.tusky.entity.Status;
 import com.keylesspalace.tusky.interfaces.AdapterItemRemover;
@@ -259,7 +260,7 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
         popup.show();
     }
 
-    protected void viewMedia(String[] urls, int urlIndex, Status.MediaAttachment.Type type,
+    protected void viewMedia(String[] urls, int urlIndex, Attachment.Type type,
                              @Nullable View view) {
         switch (type) {
             case IMAGE: {
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java
index 26c0df19..f3294d58 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java
@@ -40,6 +40,7 @@ import com.keylesspalace.tusky.BuildConfig;
 import com.keylesspalace.tusky.R;
 import com.keylesspalace.tusky.adapter.FooterViewHolder;
 import com.keylesspalace.tusky.adapter.TimelineAdapter;
+import com.keylesspalace.tusky.entity.Attachment;
 import com.keylesspalace.tusky.entity.Status;
 import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
 import com.keylesspalace.tusky.interfaces.StatusActionListener;
@@ -108,13 +109,16 @@ public class TimelineFragment extends SFragment implements
     private String bottomId;
     @Nullable
     private String topId;
+
+    private boolean alwaysShowSensitiveMedia;
+
     private PairedList<Either<Placeholder, Status>, StatusViewData> statuses =
             new PairedList<>(new Function<Either<Placeholder, Status>, StatusViewData>() {
                 @Override
                 public StatusViewData apply(Either<Placeholder, Status> input) {
                     Status status = input.getAsRightOrNull();
                     if (status != null) {
-                        return ViewDataUtils.statusToViewData(status);
+                        return ViewDataUtils.statusToViewData(status, alwaysShowSensitiveMedia);
                     } else {
                         return new StatusViewData.Placeholder(false);
                     }
@@ -150,7 +154,7 @@ public class TimelineFragment extends SFragment implements
     }
 
     @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                              Bundle savedInstanceState) {
         Bundle arguments = getArguments();
         kind = Kind.valueOf(arguments.getString(KIND_ARG));
@@ -179,6 +183,7 @@ public class TimelineFragment extends SFragment implements
         SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(
                 getActivity());
         preferences.registerOnSharedPreferenceChangeListener(this);
+        alwaysShowSensitiveMedia = preferences.getBoolean("alwaysShowSensitiveMedia", false);
         boolean mediaPreviewEnabled = preferences.getBoolean("mediaPreviewEnabled", true);
         adapter.setMediaPreviewEnabled(mediaPreviewEnabled);
         recyclerView.setAdapter(adapter);
@@ -402,7 +407,7 @@ public class TimelineFragment extends SFragment implements
     }
 
     @Override
-    public void onViewMedia(String[] urls, int urlIndex, Status.MediaAttachment.Type type,
+    public void onViewMedia(String[] urls, int urlIndex, Attachment.Type type,
                             View view) {
         super.viewMedia(urls, urlIndex, type, view);
     }
@@ -462,6 +467,10 @@ public class TimelineFragment extends SFragment implements
                 }
                 break;
             }
+            case "alwaysShowSensitiveMedia": {
+                //it is ok if only newly loaded statuses are affected, no need to fully refresh
+                alwaysShowSensitiveMedia = sharedPreferences.getBoolean("alwaysShowSensitiveMedia", false);
+            }
         }
     }
 
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java
index fc527cbc..80b0e0e6 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java
@@ -15,6 +15,7 @@
 
 package com.keylesspalace.tusky.fragment;
 
+import android.arch.core.util.Function;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.graphics.drawable.Drawable;
@@ -23,7 +24,6 @@ import android.preference.PreferenceManager;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.design.widget.Snackbar;
-import android.support.v4.content.ContextCompat;
 import android.support.v4.content.LocalBroadcastManager;
 import android.support.v4.widget.SwipeRefreshLayout;
 import android.support.v7.widget.DividerItemDecoration;
@@ -37,6 +37,7 @@ import android.view.ViewGroup;
 import com.keylesspalace.tusky.BuildConfig;
 import com.keylesspalace.tusky.R;
 import com.keylesspalace.tusky.adapter.ThreadAdapter;
+import com.keylesspalace.tusky.entity.Attachment;
 import com.keylesspalace.tusky.entity.Card;
 import com.keylesspalace.tusky.entity.Status;
 import com.keylesspalace.tusky.entity.StatusContext;
@@ -66,11 +67,17 @@ public class ViewThreadFragment extends SFragment implements
     private String thisThreadsStatusId;
     private TimelineReceiver timelineReceiver;
     private Card card;
+    private boolean alwaysShowSensitiveMedia;
 
     private int statusIndex = 0;
 
-    private final PairedList<Status, StatusViewData.Concrete> statuses =
-            new PairedList<>(ViewDataUtils.statusMapper());
+    private PairedList<Status, StatusViewData.Concrete> statuses =
+            new PairedList<>(new Function<Status, StatusViewData.Concrete>() {
+                @Override
+                public StatusViewData.Concrete apply(Status input) {
+                    return ViewDataUtils.statusToViewData(input, alwaysShowSensitiveMedia);
+                }
+            });
 
     public static ViewThreadFragment newInstance(String id) {
         Bundle arguments = new Bundle();
@@ -82,7 +89,7 @@ public class ViewThreadFragment extends SFragment implements
 
     @Nullable
     @Override
-    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                              @Nullable Bundle savedInstanceState) {
         View rootView = inflater.inflate(R.layout.fragment_view_thread, container, false);
 
@@ -96,15 +103,19 @@ public class ViewThreadFragment extends SFragment implements
         recyclerView.setLayoutManager(layoutManager);
         DividerItemDecoration divider = new DividerItemDecoration(
                 context, layoutManager.getOrientation());
-        Drawable drawable = ThemeUtils.getDrawable(context, R.attr.status_divider_drawable,
+        Drawable dividerDrawable = ThemeUtils.getDrawable(context, R.attr.status_divider_drawable,
                 R.drawable.status_divider_dark);
-        divider.setDrawable(drawable);
+        divider.setDrawable(dividerDrawable);
         recyclerView.addItemDecoration(divider);
+
+        Drawable threadLineDrawable = ThemeUtils.getDrawable(context, R.attr.conversation_thread_line_drawable,
+                R.drawable.conversation_thread_line_dark);
         recyclerView.addItemDecoration(new ConversationLineItemDecoration(context,
-                ContextCompat.getDrawable(context, R.drawable.conversation_divider_dark)));
+                threadLineDrawable));
         adapter = new ThreadAdapter(this);
         SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(
                 getActivity());
+        alwaysShowSensitiveMedia = preferences.getBoolean("alwaysShowSensitiveMedia", false);
         boolean mediaPreviewEnabled = preferences.getBoolean("mediaPreviewEnabled", true);
         adapter.setMediaPreviewEnabled(mediaPreviewEnabled);
         recyclerView.setAdapter(adapter);
@@ -157,10 +168,16 @@ public class ViewThreadFragment extends SFragment implements
                     if (status.reblog != null) {
                         status.reblog.reblogged = reblog;
                     }
-                    // create new viewData as side effect
-                    statuses.set(position, status);
 
-                    adapter.setItem(position, statuses.getPairedItem(position), true);
+                    StatusViewData.Concrete viewdata = statuses.getPairedItem(position);
+
+                    StatusViewData.Builder viewDataBuilder = new StatusViewData.Builder((viewdata));
+                    viewDataBuilder.setReblogged(reblog);
+
+                    StatusViewData.Concrete newViewData = viewDataBuilder.createStatusViewData();
+
+                    statuses.setPairedItem(position, newViewData);
+                    adapter.setItem(position, newViewData, true);
                 }
             }
 
@@ -184,9 +201,16 @@ public class ViewThreadFragment extends SFragment implements
                     if (status.reblog != null) {
                         status.reblog.favourited = favourite;
                     }
-                    // create new viewData as side effect
-                    statuses.set(position, status);
-                    adapter.setItem(position, statuses.getPairedItem(position), true);
+
+                    StatusViewData.Concrete viewdata = statuses.getPairedItem(position);
+
+                    StatusViewData.Builder viewDataBuilder = new StatusViewData.Builder((viewdata));
+                    viewDataBuilder.setFavourited(favourite);
+
+                    StatusViewData.Concrete newViewData = viewDataBuilder.createStatusViewData();
+
+                    statuses.setPairedItem(position, newViewData);
+                    adapter.setItem(position, newViewData, true);
                 }
             }
 
@@ -204,7 +228,7 @@ public class ViewThreadFragment extends SFragment implements
     }
 
     @Override
-    public void onViewMedia(String[] urls, int urlIndex, Status.MediaAttachment.Type type,
+    public void onViewMedia(String[] urls, int urlIndex, Attachment.Type type,
                             View view) {
         super.viewMedia(urls, urlIndex, type, view);
     }
@@ -367,6 +391,7 @@ public class ViewThreadFragment extends SFragment implements
                         public void onClick(View v) {
                             sendThreadRequest(id);
                             sendStatusRequest(id);
+                            sendCardRequest(id);
                         }
                     })
                     .show();
@@ -392,6 +417,7 @@ public class ViewThreadFragment extends SFragment implements
                     .setCard(card)
                     .createStatusViewData();
         }
+        statuses.setPairedItem(i, viewData);
         adapter.addItem(i, viewData);
         return i;
     }
@@ -432,6 +458,8 @@ public class ViewThreadFragment extends SFragment implements
                 viewData = new StatusViewData.Builder(viewData)
                         .setCard(card)
                         .createStatusViewData();
+                statuses.setPairedItem(statusIndex, viewData);
+
             }
             adapter.addItem(statusIndex, viewData);
         }
diff --git a/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java b/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java
index a8ff8db1..4a5d7b5c 100644
--- a/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java
+++ b/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java
@@ -17,14 +17,14 @@ package com.keylesspalace.tusky.interfaces;
 
 import android.view.View;
 
-import com.keylesspalace.tusky.entity.Status;
+import com.keylesspalace.tusky.entity.Attachment;
 
 public interface StatusActionListener extends LinkListener {
     void onReply(int position);
     void onReblog(final boolean reblog, final int position);
     void onFavourite(final boolean favourite, final int position);
     void onMore(View view, final int position);
-    void onViewMedia(String[] urls, int index, Status.MediaAttachment.Type type, View view);
+    void onViewMedia(String[] urls, int index, Attachment.Type type, View view);
     void onViewThread(int position);
     void onOpenReblog(int position);
     void onExpandedChange(boolean expanded, int position);
diff --git a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.java b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.java
index 502eeb39..c37bb4eb 100644
--- a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.java
+++ b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.java
@@ -20,8 +20,8 @@ import android.support.annotation.Nullable;
 import com.keylesspalace.tusky.entity.AccessToken;
 import com.keylesspalace.tusky.entity.Account;
 import com.keylesspalace.tusky.entity.AppCredentials;
+import com.keylesspalace.tusky.entity.Attachment;
 import com.keylesspalace.tusky.entity.Card;
-import com.keylesspalace.tusky.entity.Media;
 import com.keylesspalace.tusky.entity.Notification;
 import com.keylesspalace.tusky.entity.Profile;
 import com.keylesspalace.tusky.entity.Relationship;
@@ -80,7 +80,7 @@ public interface MastodonApi {
 
     @Multipart
     @POST("api/v1/media")
-    Call<Media> uploadMedia(@Part MultipartBody.Part file);
+    Call<Attachment> uploadMedia(@Part MultipartBody.Part file);
 
     @FormUrlEncoded
     @POST("api/v1/statuses")
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java b/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java
new file mode 100644
index 00000000..03208429
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java
@@ -0,0 +1,137 @@
+/* Copyright 2017 Andrew Dawson
+ *
+ * This file is a part of Tusky.
+ *
+ * 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.
+ *
+ * Tusky 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 Tusky; if not,
+ * see <http://www.gnu.org/licenses>. */
+
+package com.keylesspalace.tusky.util;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.SpannedString;
+import android.text.style.ReplacementSpan;
+import android.widget.TextView;
+
+import com.keylesspalace.tusky.entity.Status;
+import com.squareup.picasso.Picasso;
+import com.squareup.picasso.Target;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CustomEmojiHelper {
+
+    /**
+     * replaces emoji shortcodes in a text with EmojiSpans
+     * @param text the text containing custom emojis
+     * @param emojis a list of the custom emojis
+     * @param textView a reference to the textView the emojis will be shown in
+     * @return the text with the shortcodes replaced by EmojiSpans
+     */
+    public static Spanned emojifyText(Spanned text, List<Status.Emoji> emojis, final TextView textView) {
+
+        if (!emojis.isEmpty()) {
+
+            SpannableStringBuilder builder = new SpannableStringBuilder(text);
+            for (Status.Emoji emoji : emojis) {
+                CharSequence pattern = new StringBuilder(":").append(emoji.getShortcode()).append(':');
+                Matcher matcher = Pattern.compile(pattern.toString()).matcher(text);
+                while (matcher.find()) {
+                    // We keep a span as a Picasso target, because Picasso keeps weak reference to
+                    // the target so an anonymous class would likely be garbage collected.
+                    EmojiSpan span = new EmojiSpan(textView);
+                    builder.setSpan(span, matcher.start(), matcher.end(), 0);
+                    Picasso.with(textView.getContext())
+                            .load(emoji.getUrl())
+                            .into(span);
+                }
+            }
+
+            return builder;
+        }
+
+        return text;
+    }
+
+    public static Spanned emojifyString(String string, List<Status.Emoji> emojis, final TextView textView) {
+        return emojifyText(new SpannedString(string), emojis, textView);
+    }
+
+
+    public static class EmojiSpan extends ReplacementSpan implements Target {
+
+        private @Nullable Drawable imageDrawable;
+        private WeakReference<TextView> textViewWeakReference;
+
+        EmojiSpan(TextView textView) {
+            this.textViewWeakReference = new WeakReference<>(textView);
+        }
+
+        @Override
+        public int getSize(@NonNull Paint paint, CharSequence text, int start, int end,
+                           @Nullable Paint.FontMetricsInt fm) {
+
+            /* update FontMetricsInt or otherwise span does not get drawn when
+               it covers the whole text */
+            Paint.FontMetricsInt metrics = paint.getFontMetricsInt();
+            if (fm != null) {
+                fm.top = metrics.top;
+                fm.ascent = metrics.ascent;
+                fm.descent = metrics.descent;
+                fm.bottom = metrics.bottom;
+            }
+
+            return (int) (paint.getTextSize()*1.2);
+        }
+
+        @Override
+        public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x,
+                         int top, int y, int bottom, @NonNull Paint paint) {
+            if (imageDrawable == null) return;
+            canvas.save();
+
+            int emojiSize = (int) (paint.getTextSize() * 1.1);
+            imageDrawable.setBounds(0, 0, emojiSize, emojiSize);
+
+            int transY = bottom - imageDrawable.getBounds().bottom;
+            transY -= paint.getFontMetricsInt().descent/2;
+            canvas.translate(x, transY);
+            imageDrawable.draw(canvas);
+            canvas.restore();
+        }
+
+        @Override
+        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
+            TextView textView = textViewWeakReference.get();
+            if(textView != null) {
+                imageDrawable = new BitmapDrawable(textView.getContext().getResources(), bitmap);
+                textView.invalidate();
+            }
+        }
+
+        @Override
+        public void onBitmapFailed(Drawable errorDrawable) {}
+
+        @Override
+        public void onPrepareLoad(Drawable placeHolderDrawable) {}
+    }
+
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/CustomTabURLSpan.java b/app/src/main/java/com/keylesspalace/tusky/util/CustomTabURLSpan.java
deleted file mode 100644
index 2a25120b..00000000
--- a/app/src/main/java/com/keylesspalace/tusky/util/CustomTabURLSpan.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.keylesspalace.tusky.util;
-
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.style.URLSpan;
-import android.view.View;
-
-public class CustomTabURLSpan extends URLSpan {
-    public CustomTabURLSpan(String url) {
-        super(url);
-    }
-
-    private CustomTabURLSpan(Parcel src) {
-        super(src);
-    }
-
-    public static final Parcelable.Creator<CustomTabURLSpan> CREATOR = new Parcelable.Creator<CustomTabURLSpan>() {
-
-        @Override
-        public CustomTabURLSpan createFromParcel(Parcel source) {
-            return new CustomTabURLSpan(source);
-        }
-
-        @Override
-        public CustomTabURLSpan[] newArray(int size) {
-            return new CustomTabURLSpan[size];
-        }
-    };
-
-    @Override
-    public void onClick(View view) {
-        Uri uri = Uri.parse(getURL());
-        LinkHelper.openLinkInCustomTab(uri, view.getContext());
-    }
-}
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/CustomURLSpan.java b/app/src/main/java/com/keylesspalace/tusky/util/CustomURLSpan.java
new file mode 100644
index 00000000..e772162e
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/util/CustomURLSpan.java
@@ -0,0 +1,41 @@
+package com.keylesspalace.tusky.util;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextPaint;
+import android.text.style.URLSpan;
+import android.view.View;
+
+public class CustomURLSpan extends URLSpan {
+    public CustomURLSpan(String url) {
+        super(url);
+    }
+
+    private CustomURLSpan(Parcel src) {
+        super(src);
+    }
+
+    public static final Parcelable.Creator<CustomURLSpan> CREATOR = new Parcelable.Creator<CustomURLSpan>() {
+
+        @Override
+        public CustomURLSpan createFromParcel(Parcel source) {
+            return new CustomURLSpan(source);
+        }
+
+        @Override
+        public CustomURLSpan[] newArray(int size) {
+            return new CustomURLSpan[size];
+        }
+
+    };
+
+    @Override
+    public void onClick(View view) {
+        LinkHelper.openLink(getURL(), view.getContext());
+    }
+
+    @Override public void updateDrawState(TextPaint ds) {
+        super.updateDrawState(ds);
+        ds.setUnderlineText(false);
+    }
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java
index 19efce4e..27de3f40 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java
+++ b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java
@@ -26,6 +26,7 @@ import android.support.customtabs.CustomTabsIntent;
 import android.support.v4.content.ContextCompat;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
+import android.text.TextPaint;
 import android.text.method.LinkMovementMethod;
 import android.text.style.ClickableSpan;
 import android.text.style.URLSpan;
@@ -63,12 +64,11 @@ public class LinkHelper {
      * @param view the returned text will be put in
      * @param content containing text with mentions, links, or hashtags
      * @param mentions any '@' mentions which are known to be in the content
-     * @param useCustomTabs whether to use custom tabs when opening web links
      * @param listener to notify about particular spans that are clicked
      */
     public static void setClickableText(TextView view, Spanned content,
-            @Nullable Status.Mention[] mentions, boolean useCustomTabs,
-            final LinkListener listener) {
+            @Nullable Status.Mention[] mentions, final LinkListener listener) {
+
         SpannableStringBuilder builder = new SpannableStringBuilder(content);
         URLSpan[] urlSpans = content.getSpans(0, content.length(), URLSpan.class);
         for (URLSpan span : urlSpans) {
@@ -83,17 +83,21 @@ public class LinkHelper {
                     public void onClick(View widget) {
                         listener.onViewTag(tag);
                     }
+                    @Override public void updateDrawState(TextPaint ds) {
+                        super.updateDrawState(ds);
+                        ds.setUnderlineText(false);
+                    }
                 };
                 builder.removeSpan(span);
                 builder.setSpan(newSpan, start, end, flags);
-            } else if (text.charAt(0) == '@' && mentions != null) {
+            } else if (text.charAt(0) == '@' && mentions != null && mentions.length > 0) {
                 String accountUsername = text.subSequence(1, text.length()).toString();
                 /* There may be multiple matches for users on different instances with the same
                  * username. If a match has the same domain we know it's for sure the same, but if
                  * that can't be found then just go with whichever one matched last. */
                 String id = null;
                 for (Status.Mention mention : mentions) {
-                    if (mention.localUsername.equals(accountUsername)) {
+                    if (mention.localUsername.equalsIgnoreCase(accountUsername)) {
                         id = mention.id;
                         if (mention.url.contains(getDomain(span.getURL()))) {
                             break;
@@ -107,12 +111,16 @@ public class LinkHelper {
                         public void onClick(View widget) {
                             listener.onViewAccount(accountId);
                         }
+                        @Override public void updateDrawState(TextPaint ds) {
+                            super.updateDrawState(ds);
+                            ds.setUnderlineText(false);
+                        }
                     };
                     builder.removeSpan(span);
                     builder.setSpan(newSpan, start, end, flags);
                 }
-            } else if (useCustomTabs) {
-                ClickableSpan newSpan = new CustomTabURLSpan(span.getURL());
+            } else {
+                ClickableSpan newSpan = new CustomURLSpan(span.getURL());
                 builder.removeSpan(span);
                 builder.setSpan(newSpan, start, end, flags);
             }
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/OkHttpUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/OkHttpUtils.java
index cb9426a2..9f4fb436 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/OkHttpUtils.java
+++ b/app/src/main/java/com/keylesspalace/tusky/util/OkHttpUtils.java
@@ -85,9 +85,7 @@ public class OkHttpUtils {
 
     @NonNull
     public static OkHttpClient getCompatibleClient() {
-        OkHttpClient client = getCompatibleClientBuilder().build();
-        Log.d(TAG, client.connectTimeoutMillis()+" "+client.readTimeoutMillis()+" "+client.writeTimeoutMillis());
-        return client;
+        return getCompatibleClientBuilder().build();
     }
 
     /**
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java
index 502d558c..f3cfa708 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java
+++ b/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java
@@ -15,7 +15,6 @@
 
 package com.keylesspalace.tusky.util;
 
-import android.arch.core.util.Function;
 import android.support.annotation.Nullable;
 
 import com.keylesspalace.tusky.entity.Notification;
@@ -23,16 +22,14 @@ import com.keylesspalace.tusky.entity.Status;
 import com.keylesspalace.tusky.viewdata.NotificationViewData;
 import com.keylesspalace.tusky.viewdata.StatusViewData;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Created by charlag on 12/07/2017.
  */
 
 public final class ViewDataUtils {
     @Nullable
-    public static StatusViewData.Concrete statusToViewData(@Nullable Status status) {
+    public static StatusViewData.Concrete statusToViewData(@Nullable Status status,
+                                                           boolean alwaysShowSensitiveMedia) {
         if (status == null) return null;
         Status visibleStatus = status.reblog == null ? status : status.reblog;
         return new StatusViewData.Builder().setId(status.id)
@@ -51,6 +48,7 @@ public final class ViewDataUtils {
                 .setNickname(visibleStatus.account.username)
                 .setRebloggedAvatar(status.reblog == null ? null : status.account.avatar)
                 .setSensitive(visibleStatus.sensitive)
+                .setIsShowingSensitiveContent(alwaysShowSensitiveMedia || !visibleStatus.sensitive)
                 .setSpoilerText(visibleStatus.spoilerText)
                 .setRebloggedByUsername(status.reblog == null ? null : status.account.username)
                 .setUserFullName(visibleStatus.account.getDisplayName())
@@ -62,37 +60,9 @@ public final class ViewDataUtils {
                 .createStatusViewData();
     }
 
-    public static List<StatusViewData> statusListToViewDataList(List<Status> statuses) {
-        List<StatusViewData> viewDatas = new ArrayList<>(statuses.size());
-        for (Status s : statuses) {
-            viewDatas.add(statusToViewData(s));
-        }
-        return viewDatas;
-    }
-
-    public static Function<Status, StatusViewData.Concrete> statusMapper() {
-        return statusMapper;
-    }
-
-    public static NotificationViewData.Concrete notificationToViewData(Notification notification) {
+    public static NotificationViewData.Concrete notificationToViewData(Notification notification, boolean alwaysShowSensitiveData) {
         return new NotificationViewData.Concrete(notification.type, notification.id, notification.account,
-                statusToViewData(notification.status));
+                statusToViewData(notification.status, alwaysShowSensitiveData), false);
     }
 
-    public static List<NotificationViewData> notificationListToViewDataList(
-            List<Notification> notifications) {
-        List<NotificationViewData> viewDatas = new ArrayList<>(notifications.size());
-        for (Notification n : notifications) {
-            viewDatas.add(notificationToViewData(n));
-        }
-        return viewDatas;
-    }
-
-    private static final Function<Status, StatusViewData.Concrete> statusMapper =
-            new Function<Status, StatusViewData.Concrete>() {
-                @Override
-                public StatusViewData.Concrete apply(Status input) {
-                    return ViewDataUtils.statusToViewData(input);
-                }
-            };
 }
diff --git a/app/src/main/java/com/keylesspalace/tusky/view/ConversationLineItemDecoration.java b/app/src/main/java/com/keylesspalace/tusky/view/ConversationLineItemDecoration.java
index c90bf5f5..0beb5632 100644
--- a/app/src/main/java/com/keylesspalace/tusky/view/ConversationLineItemDecoration.java
+++ b/app/src/main/java/com/keylesspalace/tusky/view/ConversationLineItemDecoration.java
@@ -49,6 +49,7 @@ public class ConversationLineItemDecoration extends RecyclerView.ItemDecoration
 
             int position = parent.getChildAdapterPosition(child);
             ThreadAdapter adapter = (ThreadAdapter) parent.getAdapter();
+
             StatusViewData.Concrete current = adapter.getItem(position);
             int dividerTop, dividerBottom;
             if (current != null) {
@@ -59,25 +60,17 @@ public class ConversationLineItemDecoration extends RecyclerView.ItemDecoration
                     dividerTop = child.getTop() + avatarMargin;
                 }
                 StatusViewData.Concrete below = adapter.getItem(position + 1);
-                if (below != null && current.getId().equals(below.getInReplyToId())) {
+                if (below != null && current.getId().equals(below.getInReplyToId()) &&
+                        adapter.getDetailedStatusPosition() != position) {
                     dividerBottom = child.getBottom();
                 } else {
                     dividerBottom = child.getTop() + avatarMargin;
                 }
-            } else {
-                dividerTop = child.getTop();
-                if (i == 0) {
-                    dividerTop += avatarMargin;
-                }
-                if (i == childCount - 1) {
-                    dividerBottom = child.getTop() + avatarMargin;
-                } else {
-                    dividerBottom = child.getBottom();
-                }
-            }
 
-            divider.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom);
-            divider.draw(c);
+                divider.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom);
+                divider.draw(c);
+
+            }
         }
     }
 }
diff --git a/app/src/main/java/com/keylesspalace/tusky/viewdata/NotificationViewData.java b/app/src/main/java/com/keylesspalace/tusky/viewdata/NotificationViewData.java
index d0345017..43a692d9 100644
--- a/app/src/main/java/com/keylesspalace/tusky/viewdata/NotificationViewData.java
+++ b/app/src/main/java/com/keylesspalace/tusky/viewdata/NotificationViewData.java
@@ -38,13 +38,15 @@ public abstract class NotificationViewData {
         private final String id;
         private final Account account;
         private final StatusViewData.Concrete statusViewData;
+        private final boolean isExpanded;
 
         public Concrete(Notification.Type type, String id, Account account,
-                        StatusViewData.Concrete statusViewData) {
+                        StatusViewData.Concrete statusViewData, boolean isExpanded) {
             this.type = type;
             this.id = id;
             this.account = account;
             this.statusViewData = statusViewData;
+            this.isExpanded = isExpanded;
         }
 
         public Notification.Type getType() {
@@ -62,6 +64,10 @@ public abstract class NotificationViewData {
         public StatusViewData.Concrete getStatusViewData() {
             return statusViewData;
         }
+
+        public boolean isExpanded() {
+            return isExpanded;
+        }
     }
 
     public static final class Placeholder extends NotificationViewData {
diff --git a/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java b/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java
index 2d91ac36..4dfc6390 100644
--- a/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java
+++ b/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java
@@ -18,6 +18,7 @@ package com.keylesspalace.tusky.viewdata;
 import android.support.annotation.Nullable;
 import android.text.Spanned;
 
+import com.keylesspalace.tusky.entity.Attachment;
 import com.keylesspalace.tusky.entity.Card;
 import com.keylesspalace.tusky.entity.Status;
 
@@ -45,14 +46,14 @@ public abstract class StatusViewData {
         @Nullable
         private final String spoilerText;
         private final Status.Visibility visibility;
-        private final Status.MediaAttachment[] attachments;
+        private final Attachment[] attachments;
         @Nullable
         private final String rebloggedByUsername;
         @Nullable
         private final String rebloggedAvatar;
         private final boolean isSensitive;
         private final boolean isExpanded;
-        private final boolean isShowingSensitiveContent;
+        private final boolean isShowingContent;
         private final String userFullName;
         private final String nickname;
         private final String avatar;
@@ -72,9 +73,9 @@ public abstract class StatusViewData {
         private final Card card;
 
         public Concrete(String id, Spanned content, boolean reblogged, boolean favourited,
-                        @Nullable String spoilerText, Status.Visibility visibility, Status.MediaAttachment[] attachments,
+                        @Nullable String spoilerText, Status.Visibility visibility, Attachment[] attachments,
                         @Nullable String rebloggedByUsername, @Nullable String rebloggedAvatar, boolean sensitive, boolean isExpanded,
-                        boolean isShowingSensitiveWarning, String userFullName, String nickname, String avatar,
+                        boolean isShowingContent, String userFullName, String nickname, String avatar,
                         Date createdAt, String reblogsCount, String favouritesCount, @Nullable String inReplyToId,
                         @Nullable Status.Mention[] mentions, String senderId, boolean rebloggingEnabled,
                         Status.Application application, List<Status.Emoji> emojis, @Nullable Card card) {
@@ -89,7 +90,7 @@ public abstract class StatusViewData {
             this.rebloggedAvatar = rebloggedAvatar;
             this.isSensitive = sensitive;
             this.isExpanded = isExpanded;
-            this.isShowingSensitiveContent = isShowingSensitiveWarning;
+            this.isShowingContent = isShowingContent;
             this.userFullName = userFullName;
             this.nickname = nickname;
             this.avatar = avatar;
@@ -130,7 +131,7 @@ public abstract class StatusViewData {
             return visibility;
         }
 
-        public Status.MediaAttachment[] getAttachments() {
+        public Attachment[] getAttachments() {
             return attachments;
         }
 
@@ -147,8 +148,8 @@ public abstract class StatusViewData {
             return isExpanded;
         }
 
-        public boolean isShowingSensitiveContent() {
-            return isShowingSensitiveContent;
+        public boolean isShowingContent() {
+            return isShowingContent;
         }
 
         @Nullable
@@ -232,12 +233,12 @@ public abstract class StatusViewData {
         private boolean favourited;
         private String spoilerText;
         private Status.Visibility visibility;
-        private Status.MediaAttachment[] attachments;
+        private Attachment[] attachments;
         private String rebloggedByUsername;
         private String rebloggedAvatar;
         private boolean isSensitive;
         private boolean isExpanded;
-        private boolean isShowingSensitiveContent;
+        private boolean isShowingContent;
         private String userFullName;
         private String nickname;
         private String avatar;
@@ -267,7 +268,7 @@ public abstract class StatusViewData {
             rebloggedAvatar = viewData.rebloggedAvatar;
             isSensitive = viewData.isSensitive;
             isExpanded = viewData.isExpanded;
-            isShowingSensitiveContent = viewData.isShowingSensitiveContent;
+            isShowingContent = viewData.isShowingContent;
             userFullName = viewData.userFullName;
             nickname = viewData.nickname;
             avatar = viewData.avatar;
@@ -313,7 +314,7 @@ public abstract class StatusViewData {
             return this;
         }
 
-        public Builder setAttachments(Status.MediaAttachment[] attachments) {
+        public Builder setAttachments(Attachment[] attachments) {
             this.attachments = attachments;
             return this;
         }
@@ -339,7 +340,7 @@ public abstract class StatusViewData {
         }
 
         public Builder setIsShowingSensitiveContent(boolean isShowingSensitiveContent) {
-            this.isShowingSensitiveContent = isShowingSensitiveContent;
+            this.isShowingContent = isShowingSensitiveContent;
             return this;
         }
 
@@ -414,7 +415,7 @@ public abstract class StatusViewData {
 
             return new StatusViewData.Concrete(id, content, reblogged, favourited, spoilerText, visibility,
                     attachments, rebloggedByUsername, rebloggedAvatar, isSensitive, isExpanded,
-                    isShowingSensitiveContent, userFullName, nickname, avatar, createdAt, reblogsCount,
+                    isShowingContent, userFullName, nickname, avatar, createdAt, reblogsCount,
                     favouritesCount, inReplyToId, mentions, senderId, rebloggingEnabled, application,
                     emojis, card);
         }
diff --git a/app/src/main/res/drawable/conversation_divider_dark.xml b/app/src/main/res/drawable/conversation_divider_dark.xml
deleted file mode 100644
index 42034487..00000000
--- a/app/src/main/res/drawable/conversation_divider_dark.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <size android:width="2dp" />
-    <solid android:color="@color/color_primary_dark_dark" />
-</shape>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/conversation_thread_line_dark.xml b/app/src/main/res/drawable/conversation_thread_line_dark.xml
new file mode 100644
index 00000000..f19dedfb
--- /dev/null
+++ b/app/src/main/res/drawable/conversation_thread_line_dark.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <size android:width="4dp" />
+    <solid android:color="@color/status_divider_dark" />
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/conversation_thread_line_light.xml b/app/src/main/res/drawable/conversation_thread_line_light.xml
new file mode 100644
index 00000000..1d01884e
--- /dev/null
+++ b/app/src/main/res/drawable/conversation_thread_line_light.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <size android:width="4dp" />
+    <solid android:color="@color/status_divider_light" />
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/favourite_inactive_dark.xml b/app/src/main/res/drawable/favourite_inactive_dark.xml
index 24b69ac3..803131bc 100644
--- a/app/src/main/res/drawable/favourite_inactive_dark.xml
+++ b/app/src/main/res/drawable/favourite_inactive_dark.xml
@@ -1,9 +1,9 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <path
-        android:fillColor="@color/status_favourite_button_dark"
-        android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
+android:width="24dp"
+android:height="24dp"
+android:viewportWidth="24.0"
+android:viewportHeight="24.0">
+<path
+    android:fillColor="@color/status_favourite_button_dark"
+    android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
 </vector>
diff --git a/app/src/main/res/drawable/favourite_inactive_light.xml b/app/src/main/res/drawable/favourite_inactive_light.xml
index 62f63cac..06ccf499 100644
--- a/app/src/main/res/drawable/favourite_inactive_light.xml
+++ b/app/src/main/res/drawable/favourite_inactive_light.xml
@@ -1,9 +1,10 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <path
-        android:fillColor="@color/status_favourite_button_light"
-        android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
+android:width="24dp"
+android:height="24dp"
+android:viewportWidth="24.0"
+android:viewportHeight="24.0">
+<path
+    android:fillColor="@color/status_favourite_button_light"
+    android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
 </vector>
+
diff --git a/app/src/main/res/drawable/ic_person_add_24dp.xml b/app/src/main/res/drawable/ic_person_add_24dp.xml
index 5c029e12..2f4b9dd0 100644
--- a/app/src/main/res/drawable/ic_person_add_24dp.xml
+++ b/app/src/main/res/drawable/ic_person_add_24dp.xml
@@ -4,6 +4,6 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="@color/toolbar_icon_dark"
+        android:fillColor="@color/color_accent_dark"
         android:pathData="M15,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM6,10L6,7L4,7v3L1,10v2h3v3h2v-3h3v-2L6,10zM15,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
 </vector>
diff --git a/app/src/main/res/drawable/ic_play_48dp.xml b/app/src/main/res/drawable/ic_play_48dp.xml
deleted file mode 100644
index a0275bfa..00000000
--- a/app/src/main/res/drawable/ic_play_48dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:height="48dp"
-    android:width="48dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24"
-    android:autoMirrored="true">
-    <path android:fillColor="#FFF" android:pathData="M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M10,16.5L16,12L10,7.5V16.5Z" />
-</vector>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_play_indicator_dark.xml b/app/src/main/res/drawable/ic_play_indicator_dark.xml
new file mode 100644
index 00000000..62897c4d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_play_indicator_dark.xml
@@ -0,0 +1,8 @@
+<vector android:height="48dp" android:viewportHeight="24.0"
+    android:viewportWidth="24.0" android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillAlpha="1" android:fillColor="#1a1c23"
+        android:pathData="M21.282,12A9.282,9.282 0,0 1,12 21.282,9.282 9.282,0 0,1 2.718,12 9.282,9.282 0,0 1,12 2.718,9.282 9.282,0 0,1 21.282,12Z"
+        android:strokeAlpha="1" android:strokeColor="#00000000"
+        android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="8"/>
+    <path android:fillAlpha="1" android:fillColor="#2b90d9" android:pathData="M10,16.5l6,-4.5 -6,-4.5v9zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_play_indicator_light.xml b/app/src/main/res/drawable/ic_play_indicator_light.xml
new file mode 100644
index 00000000..b4c55df5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_play_indicator_light.xml
@@ -0,0 +1,15 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+android:width="48dp"
+android:height="48dp"
+android:autoMirrored="true"
+android:viewportHeight="24.0"
+android:viewportWidth="24.0">
+<path
+    android:fillAlpha="1"
+    android:fillColor="#ffff"
+    android:pathData="M21.282,12A9.282,9.282 0,0 1,12 21.282,9.282 9.282,0 0,1 2.718,12 9.282,9.282 0,0 1,12 2.718,9.282 9.282,0 0,1 21.282,12Z" />
+<path
+    android:fillAlpha="1"
+    android:fillColor="#2b90d9"
+    android:pathData="M10,16.5l6,-4.5 -6,-4.5v9zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z" />
+</vector>
diff --git a/app/src/main/res/drawable/ic_reblog_dark_18dp.xml b/app/src/main/res/drawable/ic_reblog_dark_18dp.xml
new file mode 100644
index 00000000..1a82a0f1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_reblog_dark_18dp.xml
@@ -0,0 +1,4 @@
+<vector android:height="18dp" android:viewportHeight="24.0"
+    android:viewportWidth="24.0" android:width="18dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#9baec8" android:pathData="M7,7h10v3l4,-4 -4,-4v3L5,5v6h2L7,7zM17,17L7,17v-3l-4,4 4,4v-3h12v-6h-2v4z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_reblog_light_18dp.xml b/app/src/main/res/drawable/ic_reblog_light_18dp.xml
new file mode 100644
index 00000000..03904a03
--- /dev/null
+++ b/app/src/main/res/drawable/ic_reblog_light_18dp.xml
@@ -0,0 +1,4 @@
+<vector android:height="18dp" android:viewportHeight="24.0"
+    android:viewportWidth="24.0" android:width="18dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#5f636f" android:pathData="M7,7h10v3l4,-4 -4,-4v3L5,5v6h2L7,7zM17,17L7,17v-3l-4,4 4,4v-3h12v-6h-2v4z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_reply_24dp.xml b/app/src/main/res/drawable/ic_reply_24dp.xml
index c9602ca5..31b63540 100644
--- a/app/src/main/res/drawable/ic_reply_24dp.xml
+++ b/app/src/main/res/drawable/ic_reply_24dp.xml
@@ -1,10 +1,10 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:autoMirrored="true">
+    android:width="24dp"
+    android:height="24dp"
+    android:autoMirrored="true"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0">
     <path
         android:fillColor="@color/toolbar_icon_dark"
-        android:pathData="M10,9V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z"/>
+        android:pathData="M10,9V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z" />
 </vector>
diff --git a/app/src/main/res/drawable/ic_reply_all_24dp.xml b/app/src/main/res/drawable/ic_reply_all_24dp.xml
new file mode 100644
index 00000000..8d4c8d3c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_reply_all_24dp.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:autoMirrored="true"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0">
+    <path
+        android:fillColor="@color/toolbar_icon_dark"
+        android:pathData="M7,8L7,5l-7,7 7,7v-3l-4,-4 4,-4zM13,9L13,5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z" />
+</vector>
diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml
index 16359a8d..1e111e4f 100644
--- a/app/src/main/res/layout/activity_account.xml
+++ b/app/src/main/res/layout/activity_account.xml
@@ -132,36 +132,36 @@
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         android:layout_marginBottom="8dp"
-                        app:layout_constraintBottom_toBottomOf="parent"
-                        app:layout_constraintStart_toStartOf="parent"
-                        app:layout_constraintTop_toBottomOf="@id/account_note"
-                        tools:text="3000 Followers"
                         android:background="@android:color/transparent"
                         android:textColor="@color/account_tab_font_color"
-                        app:layout_constraintHorizontal_bias="0"/>
+                        app:layout_constraintBottom_toBottomOf="parent"
+                        app:layout_constraintHorizontal_bias="0"
+                        app:layout_constraintStart_toStartOf="parent"
+                        app:layout_constraintTop_toBottomOf="@id/account_note"
+                        tools:text="3000 Followers" />
 
                     <TextView
                         android:id="@+id/following_tv"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
+                        android:textColor="@color/account_tab_font_color"
                         app:layout_constraintBottom_toBottomOf="@id/followers_tv"
                         app:layout_constraintEnd_toStartOf="@id/statuses_btn"
                         app:layout_constraintHorizontal_bias="0"
                         app:layout_constraintStart_toEndOf="@id/followers_tv"
                         app:layout_constraintTop_toTopOf="@id/followers_tv"
-                        tools:text="500 Following"
-                        android:textColor="@color/account_tab_font_color" />
+                        tools:text="500 Following" />
 
                     <TextView
                         android:id="@+id/statuses_btn"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
-                        app:layout_constraintStart_toEndOf="@id/following_tv"
+                        android:textColor="@color/account_tab_font_color"
                         app:layout_constraintEnd_toEndOf="parent"
-                        app:layout_constraintTop_toTopOf="@id/followers_tv"
-                        tools:text="3000 Posts"
                         app:layout_constraintHorizontal_bias="0"
-                        android:textColor="@color/account_tab_font_color"/>
+                        app:layout_constraintStart_toEndOf="@id/following_tv"
+                        app:layout_constraintTop_toTopOf="@id/followers_tv"
+                        tools:text="3000 Posts" />
 
                 </android.support.constraint.ConstraintLayout>
 
@@ -192,7 +192,9 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:background="?android:colorBackground"
-            app:tabSelectedTextColor="?attr/colorAccent" >
+            app:tabGravity="fill"
+            app:tabMaxWidth="0dp"
+            app:tabSelectedTextColor="?attr/colorAccent">
 
             <android.support.design.widget.TabItem
                 android:layout_width="wrap_content"
diff --git a/app/src/main/res/layout/activity_compose.xml b/app/src/main/res/layout/activity_compose.xml
index 232dfa4f..374d292d 100644
--- a/app/src/main/res/layout/activity_compose.xml
+++ b/app/src/main/res/layout/activity_compose.xml
@@ -89,7 +89,8 @@
                 android:ems="10"
                 android:gravity="start|top"
                 android:hint="@string/hint_compose"
-                android:inputType="text|textMultiLine|textCapSentences" />
+                android:inputType="text|textMultiLine|textCapSentences"
+                android:textSize="20sp" />
 
             <HorizontalScrollView
                 android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/item_follow.xml b/app/src/main/res/layout/item_follow.xml
index cceb601b..1598ccbb 100644
--- a/app/src/main/res/layout/item_follow.xml
+++ b/app/src/main/res/layout/item_follow.xml
@@ -1,87 +1,70 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
 * This is the for follow notifications, the layout for the follows/following listings on account
 * pages are instead in item_account.xml.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
+<RelativeLayout 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:paddingLeft="16dp"
-    android:paddingRight="16dp"
+    android:orientation="vertical"
     android:paddingBottom="10dp"
-    android:orientation="vertical">
+    android:paddingLeft="14dp"
+    android:paddingRight="14dp">
 
-    <RelativeLayout
-        android:layout_width="match_parent"
+    <TextView
+        android:id="@+id/notification_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="4dp"
         android:layout_marginTop="8dp"
-        android:layout_height="wrap_content">
+        android:drawableLeft="@drawable/ic_person_add_24dp"
+        android:drawablePadding="10dp"
+        android:drawableStart="@drawable/ic_person_add_24dp"
+        android:ellipsize="end"
+        android:gravity="center_vertical"
+        android:maxLines="1"
+        android:paddingLeft="28dp"
+        android:paddingStart="28dp"
+        android:textColor="?android:textColorTertiary"
+        tools:text="Someone followed you" />
 
-        <ImageView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:id="@+id/follow_icon"
-            app:srcCompat="@drawable/ic_person_add_24dp"
-            android:paddingStart="24dp"
-            android:paddingLeft="24dp"
-            android:paddingEnd="10dp"
-            android:paddingRight="10dp"
-            android:tint="?attr/colorAccent"
-            android:contentDescription="@null" />
+    <ImageView
+        android:id="@+id/notification_avatar"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentStart="true"
+        android:layout_below="@+id/notification_text"
+        android:layout_marginEnd="14dp"
+        android:layout_marginLeft="8dp"
+        android:layout_marginRight="14dp"
+        android:layout_marginStart="8dp"
+        android:contentDescription="@string/action_view_profile"
+        android:scaleType="fitCenter" />
 
-        <TextView
-            android:id="@+id/notification_text"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textColor="?android:textColorTertiary"
-            android:layout_centerVertical="true"
-            android:maxLines="1"
-            android:ellipsize="end"
-            android:layout_toEndOf="@id/follow_icon"
-            android:layout_toRightOf="@id/follow_icon" />
+    <TextView
+        android:id="@+id/notification_display_name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/notification_text"
+        android:layout_toEndOf="@id/notification_avatar"
+        android:layout_toRightOf="@id/notification_avatar"
+        android:ellipsize="end"
+        android:maxLines="1"
+        android:textColor="?android:textColorPrimary"
+        android:textStyle="normal|bold"
+        tools:text="Test User" />
 
-    </RelativeLayout>
+    <TextView
+        android:id="@+id/notification_username"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/notification_display_name"
+        android:layout_toEndOf="@id/notification_avatar"
+        android:layout_toRightOf="@id/notification_avatar"
+        android:ellipsize="end"
+        android:maxLines="1"
+        android:textColor="?android:textColorSecondary"
+        tools:text="\@testuser" />
 
-    <RelativeLayout
-        android:layout_width="match_parent"
-        android:layout_marginTop="4dp"
-        android:layout_height="wrap_content">
-
-        <ImageView
-            android:layout_width="40dp"
-            android:layout_height="40dp"
-            android:id="@+id/notification_avatar"
-            android:scaleType="fitCenter"
-            android:layout_marginStart="8dp"
-            android:layout_marginLeft="8dp"
-            android:layout_marginEnd="10dp"
-            android:layout_marginRight="10dp"
-            android:layout_alignParentStart="true"
-            android:layout_alignParentLeft="true"
-            android:contentDescription="@string/action_view_profile" />
-
-        <LinearLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:layout_toEndOf="@id/notification_avatar"
-            android:layout_toRightOf="@id/notification_avatar">
-
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:id="@+id/notification_display_name"
-                android:textColor="?android:textColorPrimary"
-                android:textStyle="normal|bold" />
-
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:id="@+id/notification_username"
-                android:textColor="?android:textColorSecondary" />
-
-        </LinearLayout>
-
-    </RelativeLayout>
-
-</LinearLayout>
\ No newline at end of file
+</RelativeLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_status.xml b/app/src/main/res/layout/item_status.xml
index 2a3182c2..68c940d5 100644
--- a/app/src/main/res/layout/item_status.xml
+++ b/app/src/main/res/layout/item_status.xml
@@ -5,48 +5,31 @@
     android:id="@+id/status_container"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:paddingLeft="16dp"
-    android:paddingRight="16dp">
+    android:paddingLeft="14dp"
+    android:paddingRight="14dp">
 
-    <LinearLayout
-        android:id="@+id/status_reblogged_bar"
+    <TextView
+        android:id="@+id/status_reblogged"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentStart="true"
-        android:layout_alignParentTop="true"
         android:layout_marginTop="@dimen/status_reblogged_bar_top_padding"
+        android:drawableLeft="?attr/status_reblog_small_drawable"
+        android:drawablePadding="6dp"
+        android:drawableStart="?attr/status_reblog_small_drawable"
         android:gravity="center_vertical"
-        android:orientation="horizontal">
-
-        <ImageView
-            android:id="@+id/status_reblogged_icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:contentDescription="@null"
-            android:paddingEnd="10dp"
-            android:paddingLeft="24dp"
-            android:paddingRight="10dp"
-            android:paddingStart="24dp"
-            android:tint="?android:textColorTertiary"
-            app:srcCompat="@drawable/ic_repeat_24dp" />
-
-        <TextView
-            android:id="@+id/status_reblogged"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textColor="?android:textColorTertiary" />
-
-    </LinearLayout>
+        android:paddingLeft="38dp"
+        android:paddingStart="38dp"
+        android:textColor="?android:textColorTertiary"
+        tools:text="ConnyDuck boosted" />
 
     <ImageView
         android:id="@+id/status_avatar"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_below="@+id/status_reblogged_bar"
-        android:layout_marginEnd="10dp"
-        android:layout_marginRight="10dp"
-        android:layout_marginTop="11dp"
+        android:layout_below="@+id/status_reblogged"
+        android:layout_marginEnd="14dp"
+        android:layout_marginRight="14dp"
+        android:layout_marginTop="14dp"
         android:contentDescription="@string/action_view_profile"
         android:scaleType="fitCenter"
         tools:src="@drawable/avatar_default" />
@@ -67,54 +50,51 @@
         android:id="@+id/status_name_bar"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_below="@+id/status_reblogged_bar"
+        android:layout_below="@+id/status_reblogged"
         android:layout_toEndOf="@+id/status_avatar"
         android:layout_toRightOf="@+id/status_avatar"
         android:paddingBottom="4dp"
         android:paddingTop="@dimen/status_avatar_padding">
 
         <TextView
-            android:id="@+id/status_timestamp"
+            android:id="@+id/status_display_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentStart="true"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:paddingEnd="@dimen/status_display_name_right_padding"
+            android:paddingLeft="0dp"
+            android:paddingRight="@dimen/status_display_name_right_padding"
+            android:paddingStart="0dp"
+            android:textColor="?android:textColorPrimary"
+            android:textStyle="normal|bold"
+            tools:text="Ente" />
+
+        <TextView
+            android:id="@+id/status_username"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toEndOf="@id/status_display_name"
+            android:layout_toLeftOf="@+id/status_timestamp_info"
+            android:layout_toRightOf="@id/status_display_name"
+            android:layout_toStartOf="@+id/status_timestamp_info"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textColor="?android:textColorSecondary"
+            tools:text="\@Entenhausen" />
+
+        <TextView
+            android:id="@+id/status_timestamp_info"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_alignParentEnd="true"
             android:layout_alignParentRight="true"
             android:layout_marginLeft="4dp"
             android:layout_marginStart="4dp"
-            android:textColor="?android:textColorSecondary" />
-
-        <LinearLayout
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentLeft="true"
-            android:layout_alignParentStart="true"
-            android:layout_toLeftOf="@id/status_timestamp"
-            android:layout_toStartOf="@id/status_timestamp"
-            android:orientation="horizontal">
-
-            <TextView
-                android:id="@+id/status_display_name"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:ellipsize="end"
-                android:maxLines="1"
-                android:paddingEnd="@dimen/status_display_name_right_padding"
-                android:paddingLeft="0dp"
-                android:paddingRight="@dimen/status_display_name_right_padding"
-                android:paddingStart="0dp"
-                android:textAppearance="@android:style/TextAppearance.DeviceDefault.Small"
-                android:textColor="?android:textColorPrimary"
-                android:textStyle="normal|bold" />
-
-            <TextView
-                android:id="@+id/status_username"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:ellipsize="end"
-                android:maxLines="1"
-                android:textColor="?android:textColorSecondary" />
-
-        </LinearLayout>
+            android:textColor="?android:textColorSecondary"
+            tools:text="13:37" />
 
     </RelativeLayout>
 
@@ -134,6 +114,7 @@
             android:id="@+id/status_content_warning_description"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:lineSpacingMultiplier="1.1"
             android:textColor="?android:textColorPrimary" />
 
         <ToggleButton
@@ -166,163 +147,177 @@
         android:layout_toEndOf="@+id/status_avatar"
         android:layout_toRightOf="@+id/status_avatar"
         android:focusable="true"
-        android:textColor="?android:textColorPrimary" />
+        android:lineSpacingMultiplier="1.1"
+        android:textColor="?android:textColorPrimary"
+        tools:text="This is a status" />
 
-    <FrameLayout
+    <android.support.constraint.ConstraintLayout
         android:id="@+id/status_media_preview_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_below="@+id/status_content"
+        android:layout_marginBottom="4dp"
+        android:layout_marginTop="@dimen/status_media_preview_top_margin"
         android:layout_toEndOf="@+id/status_avatar"
         android:layout_toRightOf="@+id/status_avatar">
 
-        <LinearLayout
-            android:layout_width="match_parent"
+        <ImageView
+            android:id="@+id/status_media_preview_0"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/status_media_preview_height"
+            android:scaleType="centerCrop"
+            app:layout_constraintEnd_toStartOf="@+id/status_media_preview_1"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_media_preview_1"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/status_media_preview_height"
+            android:layout_marginLeft="4dp"
+            android:layout_marginStart="4dp"
+            android:scaleType="centerCrop"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toEndOf="@+id/status_media_preview_0"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:ignore="ContentDescription" />
+
+
+        <ImageView
+            android:id="@+id/status_media_preview_2"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/status_media_preview_height"
+            android:layout_marginTop="4dp"
+            android:scaleType="centerCrop"
+            app:layout_constraintEnd_toStartOf="@+id/status_media_preview_3"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/status_media_preview_0"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_media_preview_3"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/status_media_preview_height"
+            android:layout_marginLeft="4dp"
+            android:layout_marginStart="4dp"
+            android:layout_marginTop="4dp"
+            android:scaleType="centerCrop"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toEndOf="@+id/status_media_preview_2"
+            app:layout_constraintTop_toBottomOf="@+id/status_media_preview_1"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_media_overlay_0"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:scaleType="center"
+            app:layout_constraintBottom_toBottomOf="@+id/status_media_preview_0"
+            app:layout_constraintEnd_toEndOf="@+id/status_media_preview_0"
+            app:layout_constraintStart_toStartOf="@+id/status_media_preview_0"
+            app:layout_constraintTop_toTopOf="@+id/status_media_preview_0"
+            app:srcCompat="?attr/play_indicator_drawable"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_media_overlay_1"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:scaleType="center"
+            app:layout_constraintBottom_toBottomOf="@+id/status_media_preview_1"
+            app:layout_constraintEnd_toEndOf="@+id/status_media_preview_1"
+            app:layout_constraintStart_toStartOf="@+id/status_media_preview_1"
+            app:layout_constraintTop_toTopOf="@+id/status_media_preview_1"
+            app:srcCompat="?attr/play_indicator_drawable"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_media_overlay_2"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:scaleType="center"
+            app:layout_constraintBottom_toBottomOf="@+id/status_media_preview_2"
+            app:layout_constraintEnd_toEndOf="@+id/status_media_preview_2"
+            app:layout_constraintStart_toStartOf="@+id/status_media_preview_2"
+            app:layout_constraintTop_toTopOf="@+id/status_media_preview_2"
+            app:srcCompat="?attr/play_indicator_drawable"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_media_overlay_3"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:scaleType="center"
+            app:layout_constraintBottom_toBottomOf="@+id/status_media_preview_3"
+            app:layout_constraintEnd_toEndOf="@+id/status_media_preview_3"
+            app:layout_constraintStart_toStartOf="@+id/status_media_preview_3"
+            app:layout_constraintTop_toTopOf="@+id/status_media_preview_3"
+            app:srcCompat="?attr/play_indicator_drawable"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_sensitive_media_button"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:orientation="vertical">
+            android:alpha="0.7"
+            android:contentDescription="@null"
+            android:padding="@dimen/status_sensitive_media_button_padding"
+            android:visibility="gone"
+            app:layout_constraintLeft_toLeftOf="@+id/status_media_preview_container"
+            app:layout_constraintTop_toTopOf="@+id/status_media_preview_container"
+            app:srcCompat="@drawable/ic_remove_red_eye_black_24dp" />
 
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginBottom="2dp"
-                android:orientation="horizontal">
-
-                <ImageView
-                    android:id="@+id/status_media_preview_0"
-                    android:layout_width="wrap_content"
-                    android:layout_height="@dimen/status_media_preview_height"
-                    android:layout_marginEnd="2dp"
-                    android:layout_marginRight="2dp"
-                    android:layout_marginTop="@dimen/status_media_preview_top_margin"
-                    android:layout_weight="1"
-                    android:contentDescription="@string/action_view_media"
-                    android:scaleType="centerCrop" />
-
-                <ImageView
-                    android:id="@+id/status_media_preview_1"
-                    android:layout_width="wrap_content"
-                    android:layout_height="@dimen/status_media_preview_height"
-                    android:layout_marginLeft="2dp"
-                    android:layout_marginStart="2dp"
-                    android:layout_marginTop="@dimen/status_media_preview_top_margin"
-                    android:layout_weight="1"
-                    android:contentDescription="@string/action_view_media"
-                    android:scaleType="centerCrop" />
-
-            </LinearLayout>
-
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="2dp"
-                android:orientation="horizontal">
-
-                <ImageView
-                    android:id="@+id/status_media_preview_2"
-                    android:layout_width="wrap_content"
-                    android:layout_height="@dimen/status_media_preview_height"
-                    android:layout_marginEnd="2dp"
-                    android:layout_marginRight="2dp"
-                    android:layout_weight="1"
-                    android:contentDescription="@string/action_view_media"
-                    android:scaleType="centerCrop" />
-
-                <ImageView
-                    android:id="@+id/status_media_preview_3"
-                    android:layout_width="wrap_content"
-                    android:layout_height="@dimen/status_media_preview_height"
-                    android:layout_marginLeft="2dp"
-                    android:layout_marginStart="2dp"
-                    android:layout_weight="1"
-                    android:contentDescription="@string/action_view_media"
-                    android:scaleType="centerCrop" />
-
-            </LinearLayout>
-
-        </LinearLayout>
-
-        <RelativeLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent">
-
-            <ImageView
-                android:id="@+id/status_video_indicator"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_centerInParent="true"
-                android:alpha="0.5"
-                android:contentDescription="@null"
-                android:visibility="gone"
-                app:srcCompat="@drawable/ic_play_48dp" />
-
-            <ImageView
-                android:id="@+id/status_sensitive_media_button"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_alignParentTop="true"
-                android:alpha="0.7"
-                android:contentDescription="@null"
-                android:padding="@dimen/status_sensitive_media_button_padding"
-                android:visibility="gone"
-                app:srcCompat="@drawable/ic_remove_red_eye_black_24dp" />
-        </RelativeLayout>
-
-        <LinearLayout
+        <TextView
             android:id="@+id/status_sensitive_media_warning"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_marginTop="@dimen/status_media_preview_top_margin"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
             android:background="?attr/sensitive_media_warning_background_color"
             android:gravity="center"
+            android:lineSpacingMultiplier="1.2"
             android:orientation="vertical"
             android:padding="8dp"
-            android:visibility="gone">
-
-            <TextView
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/status_sensitive_media_title"
-                android:textAlignment="center"
-                android:textColor="@android:color/white"
-                android:textStyle="bold" />
-
-            <TextView
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/status_sensitive_media_directions"
-                android:textAlignment="center"
-                android:textColor="@android:color/white" />
-
-        </LinearLayout>
+            android:textAlignment="center"
+            android:textColor="@android:color/white"
+            android:visibility="gone"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
 
         <TextView
             android:id="@+id/status_media_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:background="?attr/selectableItemBackground"
+            android:drawablePadding="4dp"
             android:gravity="center_vertical"
-            android:includeFontPadding="false"
             android:visibility="gone" />
 
-    </FrameLayout>
+    </android.support.constraint.ConstraintLayout>
 
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_below="@id/status_media_preview_container"
+        android:layout_marginEnd="16dp"
+        android:layout_marginRight="16dp"
         android:layout_toEndOf="@+id/status_avatar"
         android:layout_toRightOf="@+id/status_avatar"
         android:clipChildren="false"
         android:clipToPadding="false"
         android:orientation="horizontal"
-        android:paddingBottom="2dp">
+        android:paddingBottom="4dp">
 
         <ImageButton
             android:id="@+id/status_reply"
             style="?attr/image_button_style"
-            android:layout_width="40dp"
-            android:layout_height="40dp"
+            android:layout_width="24dp"
+            android:layout_height="30dp"
+            android:background="?attr/selectableItemBackgroundBorderless"
             android:contentDescription="@string/action_reply"
-            android:padding="4dp"
+            android:paddingLeft="8dp"
+            android:paddingRight="8dp"
             app:srcCompat="@drawable/ic_reply_24dp" />
 
         <Space
@@ -332,14 +327,12 @@
 
         <com.varunest.sparkbutton.SparkButton
             android:id="@+id/status_reblog"
-            android:layout_width="40dp"
-            android:layout_height="40dp"
+            android:layout_width="24dp"
+            android:layout_height="30dp"
             android:layout_gravity="center"
-            android:clipToPadding="false"
             android:contentDescription="@string/action_reblog"
-            android:padding="4dp"
             app:sparkbutton_activeImage="@drawable/reblog_active"
-            app:sparkbutton_iconSize="28dp"
+            app:sparkbutton_iconSize="24dp"
             app:sparkbutton_inActiveImage="?attr/status_reblog_inactive_drawable"
             app:sparkbutton_primaryColor="@color/status_reblog_button_marked_dark"
             app:sparkbutton_secondaryColor="@color/status_reblog_button_marked_light" />
@@ -351,14 +344,12 @@
 
         <com.varunest.sparkbutton.SparkButton
             android:id="@+id/status_favourite"
-            android:layout_width="40dp"
-            android:layout_height="40dp"
+            android:layout_width="24dp"
+            android:layout_height="30dp"
             android:layout_gravity="center"
-            android:clipToPadding="false"
             android:contentDescription="@string/action_favourite"
-            android:padding="4dp"
             app:sparkbutton_activeImage="?attr/status_favourite_active_drawable"
-            app:sparkbutton_iconSize="28dp"
+            app:sparkbutton_iconSize="24dp"
             app:sparkbutton_inActiveImage="?attr/status_favourite_inactive_drawable"
             app:sparkbutton_primaryColor="@color/status_favourite_button_marked_dark"
             app:sparkbutton_secondaryColor="@color/status_favourite_button_marked_light" />
@@ -371,17 +362,14 @@
         <ImageButton
             android:id="@+id/status_more"
             style="?attr/image_button_style"
-            android:layout_width="32dp"
-            android:layout_height="32dp"
+            android:layout_width="24dp"
+            android:layout_height="30dp"
+            android:background="?attr/selectableItemBackgroundBorderless"
             android:contentDescription="@string/action_more"
-            android:padding="4dp"
+            android:paddingLeft="6dp"
+            android:paddingRight="6dp"
             app:srcCompat="@drawable/ic_more_horiz_24dp" />
 
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1" />
-
     </LinearLayout>
 
 </RelativeLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_status_detailed.xml b/app/src/main/res/layout/item_status_detailed.xml
index 9e5ddcf9..f76b854d 100644
--- a/app/src/main/res/layout/item_status_detailed.xml
+++ b/app/src/main/res/layout/item_status_detailed.xml
@@ -5,16 +5,16 @@
     android:id="@+id/status_container"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:paddingLeft="16dp"
-    android:paddingRight="16dp">
+    android:paddingLeft="14dp"
+    android:paddingRight="14dp">
 
     <ImageView
         android:id="@+id/status_avatar"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginEnd="10dp"
-        android:layout_marginRight="10dp"
-        android:layout_marginTop="11dp"
+        android:layout_marginEnd="14dp"
+        android:layout_marginRight="14dp"
+        android:layout_marginTop="14dp"
         android:contentDescription="@string/action_view_profile"
         android:scaleType="fitCenter"
         tools:src="@drawable/avatar_default" />
@@ -22,11 +22,12 @@
     <LinearLayout
         android:id="@+id/status_name_bar"
         android:layout_width="match_parent"
-        android:layout_height="48dp"
+        android:layout_height="wrap_content"
         android:layout_marginBottom="8dp"
-        android:layout_marginTop="11dp"
+        android:layout_marginTop="14dp"
         android:layout_toEndOf="@+id/status_avatar"
         android:layout_toRightOf="@+id/status_avatar"
+        android:gravity="center_vertical"
         android:orientation="vertical">
 
         <TextView
@@ -35,22 +36,19 @@
             android:layout_height="wrap_content"
             android:ellipsize="end"
             android:maxLines="1"
-            android:textAppearance="@android:style/TextAppearance.DeviceDefault.Small"
             android:textColor="?android:textColorPrimary"
-            android:textStyle="normal|bold" />
-
-        <Space
-            android:layout_width="match_parent"
-            android:layout_height="0dp"
-            android:layout_weight="1" />
+            android:textStyle="normal|bold"
+            tools:text="Display Name" />
 
         <TextView
             android:id="@+id/status_username"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_marginTop="4dp"
             android:ellipsize="end"
             android:maxLines="1"
-            android:textColor="?android:textColorSecondary" />
+            android:textColor="?android:textColorSecondary"
+            tools:text="\@ConnyDuck\@mastodon.social" />
 
     </LinearLayout>
 
@@ -60,8 +58,6 @@
         android:layout_height="wrap_content"
         android:layout_below="@+id/status_name_bar"
         android:layout_marginBottom="4dp"
-        android:layout_toEndOf="@+id/status_avatar"
-        android:layout_toRightOf="@+id/status_avatar"
         android:focusable="true"
         android:visibility="gone"
         app:paddingHorizontal="4dp">
@@ -70,7 +66,7 @@
             android:id="@+id/status_content_warning_description"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium"
+            android:lineSpacingMultiplier="1.1"
             android:textColor="?android:textColorPrimary" />
 
         <ToggleButton
@@ -94,14 +90,10 @@
         android:id="@+id/status_content"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_alignParentEnd="true"
-        android:layout_alignParentRight="true"
         android:layout_below="@+id/status_content_warning_bar"
         android:layout_marginBottom="4dp"
-        android:layout_toEndOf="@+id/status_avatar"
-        android:layout_toRightOf="@+id/status_avatar"
         android:focusable="true"
-        android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium"
+        android:lineSpacingMultiplier="1.1"
         android:textColor="?android:textColorPrimary" />
 
     <LinearLayout
@@ -110,8 +102,6 @@
         android:layout_height="match_parent"
         android:layout_below="@+id/status_content"
         android:layout_marginTop="4dp"
-        android:layout_toEndOf="@+id/status_avatar"
-        android:layout_toRightOf="@+id/status_avatar"
         android:background="?attr/card_background"
         android:clipChildren="true"
         android:orientation="vertical">
@@ -161,184 +151,171 @@
                 android:textColor="?android:textColorTertiary" />
         </LinearLayout>
 
-
     </LinearLayout>
 
-
-    <FrameLayout
+    <android.support.constraint.ConstraintLayout
         android:id="@+id/status_media_preview_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_below="@+id/card_view"
-        android:layout_toEndOf="@+id/status_avatar"
-        android:layout_toRightOf="@+id/status_avatar">
+        android:layout_marginBottom="4dp"
+        android:layout_marginTop="@dimen/status_media_preview_top_margin">
 
-        <LinearLayout
-            android:layout_width="match_parent"
+        <ImageView
+            android:id="@+id/status_media_preview_0"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/status_media_preview_height"
+            android:scaleType="centerCrop"
+            app:layout_constraintEnd_toStartOf="@+id/status_media_preview_1"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_media_preview_1"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/status_media_preview_height"
+            android:layout_marginLeft="4dp"
+            android:layout_marginStart="4dp"
+            android:scaleType="centerCrop"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toEndOf="@+id/status_media_preview_0"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_media_preview_2"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/status_media_preview_height"
+            android:layout_marginTop="4dp"
+            android:scaleType="centerCrop"
+            app:layout_constraintEnd_toStartOf="@+id/status_media_preview_3"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/status_media_preview_0"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_media_preview_3"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/status_media_preview_height"
+            android:layout_marginLeft="4dp"
+            android:layout_marginStart="4dp"
+            android:layout_marginTop="4dp"
+            android:scaleType="centerCrop"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toEndOf="@+id/status_media_preview_2"
+            app:layout_constraintTop_toBottomOf="@+id/status_media_preview_1"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_media_overlay_0"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:scaleType="center"
+            app:layout_constraintBottom_toBottomOf="@+id/status_media_preview_0"
+            app:layout_constraintEnd_toEndOf="@+id/status_media_preview_0"
+            app:layout_constraintStart_toStartOf="@+id/status_media_preview_0"
+            app:layout_constraintTop_toTopOf="@+id/status_media_preview_0"
+            app:srcCompat="?attr/play_indicator_drawable"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_media_overlay_1"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:scaleType="center"
+            app:layout_constraintBottom_toBottomOf="@+id/status_media_preview_1"
+            app:layout_constraintEnd_toEndOf="@+id/status_media_preview_1"
+            app:layout_constraintStart_toStartOf="@+id/status_media_preview_1"
+            app:layout_constraintTop_toTopOf="@+id/status_media_preview_1"
+            app:srcCompat="?attr/play_indicator_drawable"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_media_overlay_2"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:scaleType="center"
+            app:layout_constraintBottom_toBottomOf="@+id/status_media_preview_2"
+            app:layout_constraintEnd_toEndOf="@+id/status_media_preview_2"
+            app:layout_constraintStart_toStartOf="@+id/status_media_preview_2"
+            app:layout_constraintTop_toTopOf="@+id/status_media_preview_2"
+            app:srcCompat="?attr/play_indicator_drawable"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_media_overlay_3"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:scaleType="center"
+            app:layout_constraintBottom_toBottomOf="@+id/status_media_preview_3"
+            app:layout_constraintEnd_toEndOf="@+id/status_media_preview_3"
+            app:layout_constraintStart_toStartOf="@+id/status_media_preview_3"
+            app:layout_constraintTop_toTopOf="@+id/status_media_preview_3"
+            app:srcCompat="?attr/play_indicator_drawable"
+            tools:ignore="ContentDescription" />
+
+        <ImageView
+            android:id="@+id/status_sensitive_media_button"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:orientation="vertical">
+            android:alpha="0.7"
+            android:contentDescription="@string/action_hide_media"
+            android:padding="@dimen/status_sensitive_media_button_padding"
+            android:visibility="gone"
+            app:layout_constraintLeft_toLeftOf="@+id/status_media_preview_container"
+            app:layout_constraintTop_toTopOf="@+id/status_media_preview_container"
+            app:srcCompat="@drawable/ic_remove_red_eye_black_24dp" />
 
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginBottom="2dp"
-                android:orientation="horizontal">
-
-                <ImageView
-                    android:id="@+id/status_media_preview_0"
-                    android:layout_width="wrap_content"
-                    android:layout_height="@dimen/status_media_preview_height"
-                    android:layout_marginEnd="2dp"
-                    android:layout_marginRight="2dp"
-                    android:layout_marginTop="@dimen/status_media_preview_top_margin"
-                    android:layout_weight="1"
-                    android:contentDescription="@string/action_view_media"
-                    android:scaleType="centerCrop" />
-
-                <ImageView
-                    android:id="@+id/status_media_preview_1"
-                    android:layout_width="wrap_content"
-                    android:layout_height="@dimen/status_media_preview_height"
-                    android:layout_marginLeft="2dp"
-                    android:layout_marginStart="2dp"
-                    android:layout_marginTop="@dimen/status_media_preview_top_margin"
-                    android:layout_weight="1"
-                    android:contentDescription="@string/action_view_media"
-                    android:scaleType="centerCrop" />
-
-            </LinearLayout>
-
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="2dp"
-                android:orientation="horizontal">
-
-                <ImageView
-                    android:id="@+id/status_media_preview_2"
-                    android:layout_width="wrap_content"
-                    android:layout_height="@dimen/status_media_preview_height"
-                    android:layout_marginEnd="2dp"
-                    android:layout_marginRight="2dp"
-                    android:layout_weight="1"
-                    android:contentDescription="@string/action_view_media"
-                    android:scaleType="centerCrop" />
-
-                <ImageView
-                    android:id="@+id/status_media_preview_3"
-                    android:layout_width="wrap_content"
-                    android:layout_height="@dimen/status_media_preview_height"
-                    android:layout_marginLeft="2dp"
-                    android:layout_marginStart="2dp"
-                    android:layout_weight="1"
-                    android:contentDescription="@string/action_view_media"
-                    android:scaleType="centerCrop" />
-
-            </LinearLayout>
-
-        </LinearLayout>
-
-        <RelativeLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent">
-
-            <ImageView
-                android:id="@+id/status_video_indicator"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_centerInParent="true"
-                android:alpha="0.5"
-                android:contentDescription="@null"
-                android:visibility="gone"
-                app:srcCompat="@drawable/ic_play_48dp" />
-
-            <ImageView
-                android:id="@+id/status_sensitive_media_button"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_alignParentTop="true"
-                android:alpha="0.7"
-                android:contentDescription="@null"
-                android:padding="@dimen/status_sensitive_media_button_padding"
-                android:visibility="gone"
-                app:srcCompat="@drawable/ic_remove_red_eye_black_24dp" />
-        </RelativeLayout>
-
-        <LinearLayout
+        <TextView
             android:id="@+id/status_sensitive_media_warning"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_marginTop="@dimen/status_media_preview_top_margin"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
             android:background="?attr/sensitive_media_warning_background_color"
             android:gravity="center"
+            android:lineSpacingMultiplier="1.2"
             android:orientation="vertical"
             android:padding="8dp"
-            android:visibility="gone">
-
-            <TextView
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/status_sensitive_media_title"
-                android:textAlignment="center"
-                android:textColor="@android:color/white"
-                android:textStyle="bold" />
-
-            <TextView
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/status_sensitive_media_directions"
-                android:textAlignment="center"
-                android:textColor="@android:color/white" />
-
-        </LinearLayout>
+            android:textAlignment="center"
+            android:textColor="@android:color/white"
+            android:visibility="gone"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
 
         <TextView
             android:id="@+id/status_media_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:background="?attr/selectableItemBackground"
+            android:drawablePadding="4dp"
             android:gravity="center_vertical"
-            android:includeFontPadding="false"
             android:visibility="gone" />
 
-    </FrameLayout>
+    </android.support.constraint.ConstraintLayout>
 
-    <LinearLayout
-        android:id="@+id/status_info_bar"
-        android:layout_width="match_parent"
+    <TextView
+        android:id="@+id/status_timestamp_info"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_below="@id/status_media_preview_container"
-        android:layout_marginBottom="4dp"
-        android:layout_marginTop="8dp"
-        android:layout_toEndOf="@id/status_avatar"
-        android:layout_toRightOf="@id/status_avatar"
-        android:orientation="horizontal">
-
-        <TextView
-            android:id="@+id/status_timestamp"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textColor="?android:textColorTertiary" />
-
-        <TextView
-            android:id="@+id/status_application"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="16dp"
-            android:layout_marginStart="16dp" />
-
-    </LinearLayout>
+        android:layout_marginBottom="6dp"
+        android:layout_marginTop="10dp"
+        android:textColor="?android:textColorTertiary" />
 
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_below="@id/status_info_bar"
-        android:layout_toEndOf="@id/status_avatar"
-        android:layout_toRightOf="@id/status_avatar"
+        android:layout_below="@id/status_timestamp_info"
+        android:layout_marginEnd="8dp"
+        android:layout_marginRight="8dp"
         android:clipChildren="false"
         android:clipToPadding="false"
         android:gravity="center_vertical"
         android:orientation="horizontal"
-        android:paddingBottom="2dp">
+        android:paddingBottom="4dp"
+        android:paddingTop="4dp">
 
         <ImageButton
             android:id="@+id/status_reply"
@@ -409,11 +386,6 @@
             android:padding="4dp"
             app:srcCompat="@drawable/ic_more_horiz_24dp" />
 
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1" />
-
     </LinearLayout>
 
 </RelativeLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_status_notification.xml b/app/src/main/res/layout/item_status_notification.xml
index f8e12c02..719bc796 100644
--- a/app/src/main/res/layout/item_status_notification.xml
+++ b/app/src/main/res/layout/item_status_notification.xml
@@ -6,37 +6,74 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
-    android:paddingLeft="16dp"
-    android:paddingRight="16dp">
+    android:paddingLeft="14dp"
+    android:paddingRight="14dp">
+
+    <TextView
+        android:id="@+id/notification_top_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_marginTop="8dp"
+        android:drawablePadding="10dp"
+        android:ellipsize="end"
+        android:gravity="center_vertical"
+        android:maxLines="1"
+        android:paddingLeft="28dp"
+        android:paddingStart="28dp"
+        android:textColor="?android:textColorSecondary"
+        tools:text="Someone favourited your status" />
 
     <RelativeLayout
-        android:id="@+id/notification_top_bar"
+        android:id="@+id/status_name_bar"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="8dp">
-
-        <ImageView
-            android:id="@+id/notification_icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:contentDescription="@null"
-            android:paddingEnd="10dp"
-            android:paddingLeft="24dp"
-            android:paddingRight="10dp"
-            android:paddingStart="24dp"
-            app:srcCompat="@drawable/ic_repeat_24dp" />
+        android:layout_below="@+id/notification_top_text"
+        android:layout_toEndOf="@+id/notification_status_avatar"
+        android:layout_toRightOf="@+id/notification_status_avatar"
+        android:paddingBottom="4dp"
+        android:paddingTop="6dp">
 
         <TextView
-            android:id="@+id/notification_text"
+            android:id="@+id/status_display_name"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_centerVertical="true"
-            android:layout_toEndOf="@id/notification_icon"
-            android:layout_toRightOf="@id/notification_icon"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentStart="true"
             android:ellipsize="end"
             android:maxLines="1"
-            android:textColor="?android:textColorSecondary"
-            tools:text="Someone favourited your status" />
+            android:paddingEnd="@dimen/status_display_name_right_padding"
+            android:paddingLeft="0dp"
+            android:paddingRight="@dimen/status_display_name_right_padding"
+            android:paddingStart="0dp"
+            android:textAppearance="@android:style/TextAppearance.DeviceDefault.Small"
+            android:textColor="?android:textColorTertiary"
+            android:textStyle="normal|bold"
+            tools:text="Ente" />
+
+        <TextView
+            android:id="@+id/status_username"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toEndOf="@id/status_display_name"
+            android:layout_toLeftOf="@+id/status_timestamp_info"
+            android:layout_toRightOf="@id/status_display_name"
+            android:layout_toStartOf="@+id/status_timestamp_info"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textColor="?android:textColorTertiary"
+            tools:text="\@Entenhausen" />
+
+        <TextView
+            android:id="@+id/status_timestamp_info"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:layout_alignParentRight="true"
+            android:layout_marginLeft="4dp"
+            android:layout_marginStart="4dp"
+            android:textColor="?android:textColorTertiary"
+            tools:text="13:37" />
 
     </RelativeLayout>
 
@@ -44,7 +81,7 @@
         android:id="@+id/notification_content_warning_bar"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_below="@+id/notification_top_bar"
+        android:layout_below="@+id/status_name_bar"
         android:layout_marginBottom="4dp"
         android:layout_toEndOf="@+id/notification_status_avatar"
         android:layout_toRightOf="@+id/notification_status_avatar"
@@ -57,8 +94,9 @@
             android:id="@+id/notification_content_warning_description"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:lineSpacingMultiplier="1.1"
             android:textColor="?android:textColorTertiary"
-            tools:text="Example CW text"/>
+            tools:text="Example CW text" />
 
         <ToggleButton
             android:id="@+id/notification_content_warning_button"
@@ -84,11 +122,10 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_below="@id/notification_content_warning_bar"
+        android:layout_toEndOf="@+id/notification_status_avatar"
+        android:layout_toRightOf="@+id/notification_status_avatar"
+        android:lineSpacingMultiplier="1.1"
         android:paddingBottom="10dp"
-        android:paddingEnd="0dp"
-        android:paddingLeft="58dp"
-        android:paddingRight="0dp"
-        android:paddingStart="58dp"
         android:textColor="?android:textColorTertiary"
         tools:text="Example status here" />
 
@@ -96,11 +133,11 @@
         android:id="@+id/notification_status_avatar"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_below="@id/notification_top_bar"
-        android:layout_marginBottom="8dp"
-        android:layout_marginEnd="10dp"
-        android:layout_marginRight="10dp"
-        android:layout_marginTop="11dp"
+        android:layout_below="@id/notification_top_text"
+        android:layout_marginBottom="14dp"
+        android:layout_marginEnd="14dp"
+        android:layout_marginRight="14dp"
+        android:layout_marginTop="10dp"
         android:contentDescription="@string/action_view_profile"
         android:paddingBottom="12dp"
         android:paddingRight="12dp"
@@ -114,9 +151,6 @@
         android:layout_height="24dp"
         android:layout_alignBottom="@+id/notification_status_avatar"
         android:layout_alignEnd="@id/notification_status_avatar"
-        android:layout_alignRight="@id/notification_status_avatar"
-        android:visibility="gone"
-        tools:src="@color/accent"
-        tools:visibility="visible" />
+        android:layout_alignRight="@id/notification_status_avatar" />
 
 </RelativeLayout>
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index b8f73812..d4dd2a8f 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -33,8 +33,8 @@
 
     <string name="status_username_format">\@%s</string>
     <string name="status_boosted_format">%s teilte</string>
-    <string name="status_sensitive_media_title">Sensible Medien</string>
-    <string name="status_sensitive_media_directions">Tippe um zu zeigen</string>
+    <string name="status_sensitive_media_title">Heikle Inhalte</string>
+    <string name="status_sensitive_media_directions">Zum Anzeigen tippen</string>
     <string name="status_content_warning_show_more">Zeige mehr</string>
     <string name="status_content_warning_show_less">Zeige weniger</string>
 
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 87f89247..7b40584a 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -36,8 +36,7 @@
 
     <string name="status_username_format">\@%s</string>
     <string name="status_boosted_format">%s podbił</string>
-    <string name="status_sensitive_media_title">Wrażliwe treści</string>
-    <string name="status_sensitive_media_directions">Dotknij, aby wyświetlić</string>
+    <string name="status_sensitive_media_title"><b>Wrażliwe treści</b>\nDotknij, aby wyświetlić</string>
     <string name="status_content_warning_show_more">Pokaż więcej</string>
     <string name="status_content_warning_show_less">Ukryj</string>
 
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 690ad794..a5184b55 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -14,6 +14,7 @@
     <attr name="toolbar_background_color" format="reference" />
     <attr name="toolbar_icon_tint" format="reference" />
     <attr name="image_button_style" format="reference" />
+    <attr name="status_reblog_small_drawable" format="reference" />
     <attr name="status_reblog_inactive_drawable" format="reference" />
     <attr name="status_reblog_disabled_drawable" format="reference" />
     <attr name="status_reblog_direct_drawable" format="reference" />
@@ -23,6 +24,7 @@
     <attr name="sensitive_media_warning_background_color" format="reference|color" />
     <attr name="media_preview_unloaded_drawable" format="reference" />
     <attr name="status_divider_drawable" format="reference" />
+    <attr name="conversation_thread_line_drawable" format="reference" />
     <attr name="tab_icon_selected_tint" format="reference|color" />
     <attr name="tab_page_margin_drawable" format="reference" />
     <attr name="account_header_background_color" format="reference|color" />
@@ -43,4 +45,6 @@
     <attr name="card_background" format="reference|color" />
     <attr name="card_image_background" format="reference|color" />
 
+    <attr name="play_indicator_drawable" format="reference" />
+
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index f2cb9700..a4bc41cb 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,11 +1,12 @@
 <resources>
     <dimen name="status_display_name_right_padding">4dp</dimen>
     <dimen name="status_username_right_padding">4dp</dimen>
-    <dimen name="status_avatar_padding">8dp</dimen>
+    <dimen name="status_avatar_padding">10dp</dimen>
     <dimen name="status_reblogged_bar_top_padding">8dp</dimen>
     <dimen name="status_reblogged_icon_left_padding">40dp</dimen>
     <dimen name="status_media_preview_top_margin">4dp</dimen>
-    <dimen name="status_media_preview_height">96dp</dimen>
+    <dimen name="status_media_preview_height">100dp</dimen>
+    <dimen name="status_detail_media_preview_height">130dp</dimen>
     <dimen name="footer_text_padding">8dp</dimen>
     <dimen name="compose_media_preview_margin">8dp</dimen>
     <dimen name="compose_media_preview_margin_bottom">0dp</dimen>
@@ -16,9 +17,9 @@
     <dimen name="notification_avatar_column_width">64dp</dimen>
     <dimen name="follow_icon_left_padding">40dp</dimen>
     <dimen name="account_note_margin">8dp</dimen>
-    <dimen name="account_avatar_margin">8dp</dimen>
+    <dimen name="account_avatar_margin">14dp</dimen>
     <dimen name="tab_page_margin">8dp</dimen>
-    <dimen name="status_left_line_margin">38dp</dimen>
+    <dimen name="status_left_line_margin">36dp</dimen>
     <dimen name="text_content_margin">16dp</dimen>
     <dimen name="status_sensitive_media_button_padding">5dp</dimen>
 
diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml
index 191d5a10..4419acfc 100644
--- a/app/src/main/res/values/donottranslate.xml
+++ b/app/src/main/res/values/donottranslate.xml
@@ -7,6 +7,8 @@
     <string name="oauth_redirect_host" translatable="false">com.keylesspalace.tusky</string>
     <string name="preferences_file_key" translatable="false">com.keylesspalace.tusky.PREFERENCES</string>
 
+    <string name="status_sensitive_media_template">&lt;b>%1$s&lt;/b>&lt;br>%2$s</string>
+
     <string-array name="pull_notification_check_intervals" inputType="integer">
         <item>15</item>
         <item>20</item>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 030180e2..bde69c8b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -39,7 +39,8 @@
 
     <string name="status_username_format">\@%s</string>
     <string name="status_boosted_format">%s boosted</string>
-    <string name="status_sensitive_media_title">Sensitive Media</string>
+    <string name="status_sensitive_media_title">Sensitive content</string>
+    <string name="status_media_hidden_title">Media hidden</string>
     <string name="status_sensitive_media_directions">Click to view</string>
     <string name="status_content_warning_show_more">Show More</string>
     <string name="status_content_warning_show_less">Show Less</string>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index c2f3ce63..829d8778 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -19,6 +19,7 @@
     <!--Base Application Theme Styles (Dark)-->
 
     <style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
+        <item name="android:textSize">15sp</item>
         <item name="colorPrimary">@color/color_primary_dark</item>
         <item name="colorPrimaryDark">@color/color_primary_dark_dark</item>
         <item name="colorAccent">@color/color_accent_dark</item>
@@ -40,6 +41,7 @@
         <item name="toolbar_background_color">@color/toolbar_background_dark</item>
         <item name="toolbar_icon_tint">@color/toolbar_icon_dark</item>
         <item name="image_button_style">@style/AppTheme.ImageButton.Dark</item>
+        <item name="status_reblog_small_drawable">@drawable/ic_reblog_dark_18dp</item>
         <item name="status_reblog_inactive_drawable">@drawable/reblog_inactive_dark</item>
         <item name="status_reblog_disabled_drawable">@drawable/reblog_disabled_dark</item>
         <item name="status_reblog_direct_drawable">@drawable/reblog_direct_dark</item>
@@ -49,6 +51,7 @@
         <item name="sensitive_media_warning_background_color">@color/color_background_dark</item>
         <item name="media_preview_unloaded_drawable">@drawable/media_preview_unloaded_dark</item>
         <item name="status_divider_drawable">@drawable/status_divider_dark</item>
+        <item name="conversation_thread_line_drawable">@drawable/conversation_thread_line_dark</item>
         <item name="tab_icon_selected_tint">@color/color_accent_dark</item>
         <item name="tab_page_margin_drawable">@drawable/tab_page_margin_dark</item>
         <item name="account_header_background_color">@color/account_header_background_dark</item>
@@ -81,6 +84,8 @@
         <item name="card_background">@drawable/card_frame_dark</item>
         <item name="card_image_background">@color/text_color_tertiary_dark</item>
 
+        <item name="play_indicator_drawable">@drawable/ic_play_indicator_dark</item>
+
     </style>
 
     <style name="AppTheme.ImageButton.Dark" parent="@style/Widget.AppCompat.Button.Borderless.Colored">
@@ -101,6 +106,8 @@
     <!--Light Application Theme Styles-->
 
     <style name="AppTheme.Light" parent="Theme.AppCompat.Light.NoActionBar">
+        <item name="android:textSize">15sp</item>
+
         <item name="colorPrimary">@color/color_primary_light</item>
         <item name="colorPrimaryDark">@color/color_primary_dark_light</item>
         <item name="colorAccent">@color/color_accent_light</item>
@@ -122,6 +129,7 @@
         <item name="toolbar_background_color">@color/toolbar_background_light</item>
         <item name="toolbar_icon_tint">@color/toolbar_icon_light</item>
         <item name="image_button_style">@style/AppTheme.ImageButton.Light</item>
+        <item name="status_reblog_small_drawable">@drawable/ic_reblog_light_18dp</item>
         <item name="status_reblog_inactive_drawable">@drawable/reblog_inactive_light</item>
         <item name="status_reblog_disabled_drawable">@drawable/reblog_disabled_light</item>
         <item name="status_reblog_direct_drawable">@drawable/reblog_direct_light</item>
@@ -131,6 +139,7 @@
         <item name="sensitive_media_warning_background_color">@color/sensitive_media_warning_background_light</item>
         <item name="media_preview_unloaded_drawable">@drawable/media_preview_unloaded_light</item>
         <item name="status_divider_drawable">@drawable/status_divider_light</item>
+        <item name="conversation_thread_line_drawable">@drawable/conversation_thread_line_light</item>
         <item name="tab_icon_selected_tint">@color/color_accent_light</item>
         <item name="tab_page_margin_drawable">@drawable/tab_page_margin_light</item>
         <item name="account_header_background_color">@color/account_header_background_light</item>
@@ -163,6 +172,8 @@
         <item name="card_background">@drawable/card_frame_light</item>
         <item name="card_image_background">@color/text_color_tertiary_light</item>
 
+        <item name="play_indicator_drawable">@drawable/ic_play_indicator_light</item>
+
     </style>
 
     <style name="AppTheme.ImageButton.Light" parent="Widget.AppCompat.Button.Borderless.Colored">
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 4fedafec..40b4be60 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -21,6 +21,7 @@
 
         <CheckBoxPreference
             android:defaultValue="false"
+            android:dependency="mediaPreviewEnabled"
             android:key="alwaysShowSensitiveMedia"
             android:title="@string/pref_title_alway_show_sensitive_media" />