Fix/emoji reactions crash (#1646)

* Fixing crash on sdk =28 for emoji reactions

* Proper styling of reactions

Making sure we display the public key when there is no name

* Making sure we display the emoji that was long pressed
This commit is contained in:
ThomasSession 2024-08-28 09:17:15 +10:00 committed by GitHub
parent 1393335121
commit d1c4283f42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 112 additions and 45 deletions

View File

@ -1529,13 +1529,13 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
} }
} }
override fun onReactionLongClicked(messageId: MessageId) { override fun onReactionLongClicked(messageId: MessageId, emoji: String?) {
if (viewModel.recipient?.isGroupRecipient == true) { if (viewModel.recipient?.isGroupRecipient == true) {
val isUserModerator = viewModel.openGroup?.let { openGroup -> val isUserModerator = viewModel.openGroup?.let { openGroup ->
val userPublicKey = textSecurePreferences.getLocalNumber() ?: return@let false val userPublicKey = textSecurePreferences.getLocalNumber() ?: return@let false
OpenGroupManager.isUserModerator(this, openGroup.id, userPublicKey, viewModel.blindedPublicKey) OpenGroupManager.isUserModerator(this, openGroup.id, userPublicKey, viewModel.blindedPublicKey)
} ?: false } ?: false
val fragment = ReactionsDialogFragment.create(messageId, isUserModerator) val fragment = ReactionsDialogFragment.create(messageId, isUserModerator, emoji)
fragment.show(supportFragmentManager, null) fragment.show(supportFragmentManager, null)
} }
} }

View File

@ -10,6 +10,7 @@ import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.setPadding
import com.google.android.flexbox.JustifyContent import com.google.android.flexbox.JustifyContent
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.ViewEmojiReactionsBinding import network.loki.messenger.databinding.ViewEmojiReactionsBinding
@ -43,6 +44,8 @@ class EmojiReactionsView : ConstraintLayout, OnTouchListener {
private var onDownTimestamp: Long = 0 private var onDownTimestamp: Long = 0
private var extended = false private var extended = false
private val overflowItemSize = ViewUtil.dpToPx(24)
constructor(context: Context) : super(context) { init(null) } constructor(context: Context) : super(context) { init(null) }
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { init(attrs) } constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { init(attrs) }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { init(attrs) } constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { init(attrs) }
@ -81,7 +84,9 @@ class EmojiReactionsView : ConstraintLayout, OnTouchListener {
if (v.tag == null) return false if (v.tag == null) return false
val reaction = v.tag as Reaction val reaction = v.tag as Reaction
val action = event.action val action = event.action
if (action == MotionEvent.ACTION_DOWN) onDown(MessageId(reaction.messageId, reaction.isMms)) else if (action == MotionEvent.ACTION_CANCEL) removeLongPressCallback() else if (action == MotionEvent.ACTION_UP) onUp(reaction) if (action == MotionEvent.ACTION_DOWN) onDown(MessageId(reaction.messageId, reaction.isMms), reaction.emoji)
else if (action == MotionEvent.ACTION_CANCEL) removeLongPressCallback()
else if (action == MotionEvent.ACTION_UP) onUp(reaction)
return true return true
} }
@ -91,18 +96,15 @@ class EmojiReactionsView : ConstraintLayout, OnTouchListener {
binding.layoutEmojiContainer.removeAllViews() binding.layoutEmojiContainer.removeAllViews()
val overflowContainer = LinearLayout(context) val overflowContainer = LinearLayout(context)
overflowContainer.orientation = LinearLayout.HORIZONTAL overflowContainer.orientation = LinearLayout.HORIZONTAL
val innerPadding = ViewUtil.dpToPx(4)
overflowContainer.setPaddingRelative(innerPadding, innerPadding, innerPadding, innerPadding)
val pixelSize = ViewUtil.dpToPx(1) val pixelSize = ViewUtil.dpToPx(1)
for (reaction in reactions) { reactions.forEachIndexed { index, reaction ->
if (binding.layoutEmojiContainer.childCount + 1 >= DEFAULT_THRESHOLD && threshold != Int.MAX_VALUE && reactions.size > threshold) { if (binding.layoutEmojiContainer.childCount + 1 >= DEFAULT_THRESHOLD && threshold != Int.MAX_VALUE && reactions.size > threshold) {
if (overflowContainer.parent == null) { if (overflowContainer.parent == null) {
binding.layoutEmojiContainer.addView(overflowContainer) binding.layoutEmojiContainer.addView(overflowContainer)
val overflowParams = overflowContainer.layoutParams as MarginLayoutParams val overflowParams = overflowContainer.layoutParams as MarginLayoutParams
overflowParams.height = ViewUtil.dpToPx(26) overflowParams.height = MarginLayoutParams.WRAP_CONTENT
overflowParams.setMargins(pixelSize, pixelSize, pixelSize, pixelSize) overflowParams.setMargins(pixelSize, pixelSize, pixelSize, pixelSize)
overflowContainer.layoutParams = overflowParams overflowContainer.layoutParams = overflowParams
overflowContainer.background = ContextCompat.getDrawable(context, R.drawable.reaction_pill_background)
} }
val pill = buildPill(context, this, reaction, true) val pill = buildPill(context, this, reaction, true)
pill.setOnClickListener { v: View? -> pill.setOnClickListener { v: View? ->
@ -111,6 +113,7 @@ class EmojiReactionsView : ConstraintLayout, OnTouchListener {
} }
pill.findViewById<View>(R.id.reactions_pill_count).visibility = GONE pill.findViewById<View>(R.id.reactions_pill_count).visibility = GONE
pill.findViewById<View>(R.id.reactions_pill_spacer).visibility = GONE pill.findViewById<View>(R.id.reactions_pill_spacer).visibility = GONE
pill.z = reaction.count - index.toFloat() // make sure the overflow is stacked properly
overflowContainer.addView(pill) overflowContainer.addView(pill)
} else { } else {
val pill = buildPill(context, this, reaction, false) val pill = buildPill(context, this, reaction, false)
@ -179,9 +182,10 @@ class EmojiReactionsView : ConstraintLayout, OnTouchListener {
val countView = root.findViewById<TextView>(R.id.reactions_pill_count) val countView = root.findViewById<TextView>(R.id.reactions_pill_count)
val spacer = root.findViewById<View>(R.id.reactions_pill_spacer) val spacer = root.findViewById<View>(R.id.reactions_pill_spacer)
if (isCompact) { if (isCompact) {
root.setPaddingRelative(1, 1, 1, 1) root.setPadding(0)
val layoutParams = root.layoutParams val layoutParams = root.layoutParams
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT layoutParams.height = overflowItemSize
layoutParams.width = overflowItemSize
root.layoutParams = layoutParams root.layoutParams = layoutParams
} }
if (reaction.emoji != null) { if (reaction.emoji != null) {
@ -201,9 +205,8 @@ class EmojiReactionsView : ConstraintLayout, OnTouchListener {
root.background = ContextCompat.getDrawable(context, R.drawable.reaction_pill_background_selected) root.background = ContextCompat.getDrawable(context, R.drawable.reaction_pill_background_selected)
countView.setTextColor(ThemeUtil.getThemedColor(context, R.attr.reactionsPillSelectedTextColor)) countView.setTextColor(ThemeUtil.getThemedColor(context, R.attr.reactionsPillSelectedTextColor))
} else { } else {
if (!isCompact) { root.background = if(isCompact) ContextCompat.getDrawable(context, R.drawable.reaction_pill_background_bordered)
root.background = ContextCompat.getDrawable(context, R.drawable.reaction_pill_background) else ContextCompat.getDrawable(context, R.drawable.reaction_pill_background)
}
} }
return root return root
} }
@ -215,12 +218,12 @@ class EmojiReactionsView : ConstraintLayout, OnTouchListener {
} }
} }
private fun onDown(messageId: MessageId) { private fun onDown(messageId: MessageId, emoji: String?) {
removeLongPressCallback() removeLongPressCallback()
val newLongPressCallback = Runnable { val newLongPressCallback = Runnable {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
if (delegate != null) { if (delegate != null) {
delegate!!.onReactionLongClicked(messageId) delegate!!.onReactionLongClicked(messageId, emoji)
} }
} }
longPressCallback = newLongPressCallback longPressCallback = newLongPressCallback

View File

@ -10,6 +10,6 @@ interface VisibleMessageViewDelegate {
fun onReactionClicked(emoji: String, messageId: MessageId, userWasSender: Boolean) fun onReactionClicked(emoji: String, messageId: MessageId, userWasSender: Boolean)
fun onReactionLongClicked(messageId: MessageId) fun onReactionLongClicked(messageId: MessageId, emoji: String?)
} }

View File

@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.reactions; package org.thoughtcrime.securesms.reactions;
import static org.session.libsession.utilities.IdUtilKt.truncateIdForDisplay;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -129,7 +131,7 @@ final class ReactionRecipientsAdapter extends RecyclerView.Adapter<ReactionRecip
EmojiImageView emojiView = itemView.findViewById(R.id.header_view_emoji); EmojiImageView emojiView = itemView.findViewById(R.id.header_view_emoji);
emojiView.setImageEmoji(emoji.getDisplayEmoji()); emojiView.setImageEmoji(emoji.getDisplayEmoji());
TextView count = itemView.findViewById(R.id.header_view_emoji_count); TextView count = itemView.findViewById(R.id.header_view_emoji_count);
count.setText(String.format(" · %s", emoji.getCount())); count.setText(String.format(" %s", emoji.getCount()));
} }
} }
@ -161,8 +163,8 @@ final class ReactionRecipientsAdapter extends RecyclerView.Adapter<ReactionRecip
this.remove.setVisibility(View.VISIBLE); this.remove.setVisibility(View.VISIBLE);
} else { } else {
String name = reaction.getSender().getName(); String name = reaction.getSender().getName();
if (name != null && new AccountId(name).getPrefix() != null) { if(name == null){
name = name.substring(0, 4) + "..." + name.substring(name.length() - 4); name = truncateIdForDisplay(reaction.getSender().getAddress().serialize());
} }
this.recipient.setText(name); this.recipient.setText(name);
this.remove.setVisibility(View.GONE); this.remove.setVisibility(View.GONE);

View File

@ -99,7 +99,7 @@ class ReactionViewPagerAdapter extends ListAdapter<EmojiCount, ReactionViewPager
recycler.setLayoutParams(params); recycler.setLayoutParams(params);
DividerItemDecoration decoration = new DividerItemDecoration(itemView.getContext(), LinearLayoutManager.VERTICAL); DividerItemDecoration decoration = new DividerItemDecoration(itemView.getContext(), LinearLayoutManager.VERTICAL);
decoration.setDrawable(ContextUtil.requireDrawable(itemView.getContext(), R.drawable.vertical_divider)); decoration.setDrawable(ContextUtil.requireDrawable(itemView.getContext(), R.drawable.horizontal_divider));
recycler.addItemDecoration(decoration); recycler.addItemDecoration(decoration);
recycler.setAdapter(adapter); recycler.setAdapter(adapter);
} }

View File

@ -21,6 +21,7 @@ import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator; import com.google.android.material.tabs.TabLayoutMediator;
import org.session.libsession.utilities.ThemeUtil; import org.session.libsession.utilities.ThemeUtil;
import org.session.libsignal.utilities.Log;
import org.thoughtcrime.securesms.components.emoji.EmojiImageView; import org.thoughtcrime.securesms.components.emoji.EmojiImageView;
import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.util.LifecycleDisposable; import org.thoughtcrime.securesms.util.LifecycleDisposable;
@ -35,6 +36,7 @@ public final class ReactionsDialogFragment extends BottomSheetDialogFragment imp
private static final String ARGS_MESSAGE_ID = "reactions.args.message.id"; private static final String ARGS_MESSAGE_ID = "reactions.args.message.id";
private static final String ARGS_IS_MMS = "reactions.args.is.mms"; private static final String ARGS_IS_MMS = "reactions.args.is.mms";
private static final String ARGS_IS_MODERATOR = "reactions.args.is.moderator"; private static final String ARGS_IS_MODERATOR = "reactions.args.is.moderator";
private static final String ARGS_EMOJI = "reactions.args.emoji";
private ViewPager2 recipientPagerView; private ViewPager2 recipientPagerView;
private ReactionViewPagerAdapter recipientsAdapter; private ReactionViewPagerAdapter recipientsAdapter;
@ -42,13 +44,14 @@ public final class ReactionsDialogFragment extends BottomSheetDialogFragment imp
private final LifecycleDisposable disposables = new LifecycleDisposable(); private final LifecycleDisposable disposables = new LifecycleDisposable();
public static DialogFragment create(MessageId messageId, boolean isUserModerator) { public static DialogFragment create(MessageId messageId, boolean isUserModerator, @Nullable String emoji) {
Bundle args = new Bundle(); Bundle args = new Bundle();
DialogFragment fragment = new ReactionsDialogFragment(); DialogFragment fragment = new ReactionsDialogFragment();
args.putLong(ARGS_MESSAGE_ID, messageId.getId()); args.putLong(ARGS_MESSAGE_ID, messageId.getId());
args.putBoolean(ARGS_IS_MMS, messageId.isMms()); args.putBoolean(ARGS_IS_MMS, messageId.isMms());
args.putBoolean(ARGS_IS_MODERATOR, isUserModerator); args.putBoolean(ARGS_IS_MODERATOR, isUserModerator);
args.putString(ARGS_EMOJI, emoji);
fragment.setArguments(args); fragment.setArguments(args);
@ -68,7 +71,6 @@ public final class ReactionsDialogFragment extends BottomSheetDialogFragment imp
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
// setStyle(DialogFragment.STYLE_NORMAL, R.style.Theme_Session_BottomSheet);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
} }
@ -102,7 +104,7 @@ public final class ReactionsDialogFragment extends BottomSheetDialogFragment imp
ViewCompat.setOnApplyWindowInsetsListener(container, (v, insets) -> insets.consumeSystemWindowInsets()); ViewCompat.setOnApplyWindowInsetsListener(container, (v, insets) -> insets.consumeSystemWindowInsets());
TabLayoutMediator mediator = new TabLayoutMediator(emojiTabs, recipientPagerView, (tab, position) -> { TabLayoutMediator mediator = new TabLayoutMediator(emojiTabs, recipientPagerView, (tab, position) -> {
tab.setCustomView(R.layout.reactions_pill); tab.setCustomView(R.layout.reactions_pill_large);
View customView = Objects.requireNonNull(tab.getCustomView()); View customView = Objects.requireNonNull(tab.getCustomView());
EmojiImageView emoji = customView.findViewById(R.id.reactions_pill_emoji); EmojiImageView emoji = customView.findViewById(R.id.reactions_pill_emoji);
@ -169,6 +171,18 @@ public final class ReactionsDialogFragment extends BottomSheetDialogFragment imp
} }
recipientsAdapter.submitList(emojiCounts); recipientsAdapter.submitList(emojiCounts);
// select the tab based on which emoji the user long pressed on
TabLayout emojiTabs = requireDialog().findViewById(R.id.emoji_tabs);
String emoji = requireArguments().getString(ARGS_EMOJI);
int tabIndex = 0;
for (int i = 0; i < emojiCounts.size(); i++) {
if(emojiCounts.get(i).getBaseEmoji().equals(emoji)){
tabIndex = i;
break;
}
}
emojiTabs.selectTab(emojiTabs.getTabAt(tabIndex));
})); }));
} }

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <shape xmlns:android="http://schemas.android.com/apk/res/android">
<size android:height="1dp"/> <size android:height="1dp"/>
<solid android:color="?android:textColorTertiary"/> <solid android:color="?colorDividerBackground"/>
</shape> </shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="1000dp" />
<solid android:color="?reactionsPillNormalBackground" />
<stroke android:width="1dp" android:color="?colorPrimary" />
</shape>

View File

@ -3,7 +3,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="52dp"> android:layout_height="50dp"
android:background="?backgroundSecondary">
<org.thoughtcrime.securesms.components.ProfilePictureView <org.thoughtcrime.securesms.components.ProfilePictureView
android:id="@+id/reactions_bottom_view_avatar" android:id="@+id/reactions_bottom_view_avatar"
@ -22,7 +23,7 @@
android:ellipsize="end" android:ellipsize="end"
android:gravity="center_vertical" android:gravity="center_vertical"
android:maxLines="2" android:maxLines="2"
android:textAppearance="@style/TextAppearance.AppCompat.Body2" style="@style/Signal.Text.Preview"
android:textColor="?android:textColorPrimary" android:textColor="?android:textColorPrimary"
app:layout_constrainedWidth="true" app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"

View File

@ -1,9 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android" <TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="50dp"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center_horizontal" android:gravity="center"
android:padding="@dimen/small_spacing"
android:id="@+id/footer_view_emoji_count" android:id="@+id/footer_view_emoji_count"
android:layout_weight="1" /> android:layout_weight="1"
style="@style/Signal.Text.Preview"
android:textColor="?android:textColorTertiary"
tools:text="And 1244 other have reacted to this message" />

View File

@ -6,21 +6,21 @@
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<View <View
android:layout_gravity="top" android:layout_gravity="top"
android:alpha="0.3"
android:layout_marginHorizontal="@dimen/small_spacing" android:layout_marginHorizontal="@dimen/small_spacing"
android:background="?android:textColorTertiary" android:background="?colorDividerBackground"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp"/> android:layout_height="1dp"/>
<LinearLayout android:layout_height="wrap_content" <LinearLayout android:layout_height="43dp"
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_marginHorizontal="@dimen/medium_spacing" android:layout_marginHorizontal="@dimen/medium_spacing"
android:padding="@dimen/small_spacing"> android:gravity="center_vertical">
<org.thoughtcrime.securesms.components.emoji.EmojiImageView <org.thoughtcrime.securesms.components.emoji.EmojiImageView
android:id="@+id/header_view_emoji" android:id="@+id/header_view_emoji"
android:layout_width="20dp" android:layout_width="20dp"
android:layout_height="20dp" android:layout_height="20dp"
android:layout_marginEnd="4dp" android:layout_marginEnd="4dp"
android:layout_gravity="center_vertical"
android:gravity="center" android:gravity="center"
android:textStyle="bold" android:textStyle="bold"
app:emoji_forceCustom="true" /> app:emoji_forceCustom="true" />
@ -28,13 +28,16 @@
android:id="@+id/header_view_emoji_count" android:id="@+id/header_view_emoji_count"
android:layout_weight="1" android:layout_weight="1"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="?android:textColorTertiary"/>
<TextView <TextView
android:text="@string/message_requests_clear_all" android:text="@string/message_requests_clear_all"
android:textColor="?danger" android:textColor="?danger"
android:visibility="gone" android:visibility="gone"
android:id="@+id/header_view_clear_all" android:id="@+id/header_view_clear_all"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"
android:layout_gravity="center_vertical"/>
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>

View File

@ -4,31 +4,31 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="26dp" android:layout_height="22dp"
android:paddingStart="7dp" android:paddingStart="8dp"
android:paddingEnd="7dp" android:paddingEnd="8dp"
android:gravity="center"> android:gravity="center">
<org.thoughtcrime.securesms.components.emoji.EmojiImageView <org.thoughtcrime.securesms.components.emoji.EmojiImageView
android:scaleType="centerInside" android:scaleType="centerInside"
android:id="@+id/reactions_pill_emoji" android:id="@+id/reactions_pill_emoji"
android:layout_width="17dp" android:layout_width="13dp"
android:layout_height="17dp" /> android:layout_height="13dp"
android:layout_gravity="center_vertical"/>
<View <View
android:id="@+id/reactions_pill_spacer" android:id="@+id/reactions_pill_spacer"
android:layout_width="4dp" android:layout_width="6dp"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<TextView <TextView
android:id="@+id/reactions_pill_count" android:id="@+id/reactions_pill_count"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="5dp" android:textSize="11sp"
android:paddingBottom="5dp"
android:textSize="12dp"
android:fontFamily="sans-serif-medium" android:fontFamily="sans-serif-medium"
android:textColor="?android:textColorTertiary" android:textColor="?android:textColorTertiary"
android:layout_gravity="center_vertical"
tools:text="23" tools:text="23"
tools:ignore="SpUsage" /> tools:ignore="SpUsage" />

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:gravity="center">
<org.thoughtcrime.securesms.components.emoji.EmojiImageView
android:scaleType="centerInside"
android:id="@+id/reactions_pill_emoji"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center_vertical"/>
<View
android:id="@+id/reactions_pill_spacer"
android:layout_width="10dp"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/reactions_pill_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:fontFamily="sans-serif-medium"
android:textColor="?android:textColorTertiary"
android:layout_gravity="center_vertical"
tools:text="23"
tools:ignore="SpUsage" />
</LinearLayout>