Implement bottom selection menu in chat.

This commit is contained in:
Rashad Sookram
2022-01-07 11:47:57 -05:00
committed by Alex Hart
parent a8d9933265
commit 829c06ab1a
14 changed files with 172 additions and 120 deletions

View File

@@ -96,6 +96,7 @@ public class InputPanel extends LinearLayout
private boolean hideForGroupState;
private boolean hideForBlockedState;
private boolean hideForSearch;
private boolean hideForSelection;
private ConversationStickerSuggestionAdapter stickerSuggestionAdapter;
@@ -336,6 +337,11 @@ public class InputPanel extends LinearLayout
updateVisibility();
}
public void setHideForSelection(boolean hideForSelection) {
this.hideForSelection = hideForSelection;
updateVisibility();
}
@Override
public void onRecordPermissionRequired() {
if (listener != null) listener.onRecorderPermissionRequired();
@@ -515,7 +521,7 @@ public class InputPanel extends LinearLayout
}
private void updateVisibility() {
if (hideForGroupState || hideForBlockedState || hideForSearch) {
if (hideForGroupState || hideForBlockedState || hideForSearch || hideForSelection) {
setVisibility(GONE);
} else {
setVisibility(VISIBLE);

View File

@@ -3775,6 +3775,11 @@ public class ConversationActivity extends PassphraseRequiredActivity
searchViewItem.collapseActionView();
}
@Override
public void onBottomActionBarVisibilityChanged(int visibility) {
inputPanel.setHideForSelection(visibility == View.VISIBLE);
}
@Override
public void onForwardClicked() {
inputPanel.clearQuote();

View File

@@ -25,6 +25,7 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Rect;
import android.net.Uri;
import android.os.AsyncTask;
@@ -33,7 +34,6 @@ import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@@ -65,16 +65,19 @@ import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
import org.signal.core.util.DimensionUnit;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.LoggingFragment;
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.menu.ActionItem;
import org.thoughtcrime.securesms.components.menu.SignalBottomActionBar;
import org.thoughtcrime.securesms.verify.VerifyIdentityActivity;
import org.thoughtcrime.securesms.components.ConversationScrollToView;
import org.thoughtcrime.securesms.components.ConversationTypingView;
import org.thoughtcrime.securesms.components.TooltipPopup;
import org.thoughtcrime.securesms.components.TypingStatusRepository;
import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager;
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
@@ -162,12 +165,12 @@ import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.WindowUtil;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.thoughtcrime.securesms.util.views.AdaptiveActionsToolbar;
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -224,6 +227,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
private LayoutTransition layoutTransition;
private TransitionListener transitionListener;
private View reactionsShade;
private SignalBottomActionBar bottomActionBar;
private GiphyMp4ProjectionRecycler giphyMp4ProjectionRecycler;
private Colorizer colorizer;
@@ -265,6 +269,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
scrollDateHeader = view.findViewById(R.id.scroll_date_header);
toolbarShadow = requireActivity().findViewById(R.id.conversation_toolbar_shadow);
reactionsShade = view.findViewById(R.id.reactions_shade);
bottomActionBar = view.findViewById(R.id.conversation_bottom_action_bar);
final LinearLayoutManager layoutManager = new SmoothScrollingLinearLayoutManager(getActivity(), true);
final ConversationItemAnimator conversationItemAnimator = new ConversationItemAnimator(
@@ -283,6 +288,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
() -> conversationViewModel.getWallpaper().getValue());
list.setHasFixedSize(false);
layoutManager.setStackFromEnd(true);
list.setLayoutManager(layoutManager);
RecyclerViewColorizer recyclerViewColorizer = new RecyclerViewColorizer(list);
@@ -739,7 +745,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
});
}
private void setCorrectActionModeMenuVisibility(@NonNull Menu menu) {
private void setCorrectActionModeMenuVisibility() {
Set<MultiselectPart> selectedParts = getListAdapter().getSelectedItems();
if (actionMode != null && selectedParts.size() == 0) {
@@ -747,17 +753,56 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
return;
}
setBottomActionBarVisibility(true);
MenuState menuState = MenuState.getMenuState(recipient.get(), selectedParts, messageRequestViewModel.shouldShowMessageRequest(), groupViewModel.isNonAdminInAnnouncementGroup());
menu.findItem(R.id.menu_context_forward).setVisible(menuState.shouldShowForwardAction());
menu.findItem(R.id.menu_context_reply).setVisible(menuState.shouldShowReplyAction());
menu.findItem(R.id.menu_context_details).setVisible(menuState.shouldShowDetailsAction());
menu.findItem(R.id.menu_context_save_attachment).setVisible(menuState.shouldShowSaveAttachmentAction());
menu.findItem(R.id.menu_context_resend).setVisible(menuState.shouldShowResendAction());
menu.findItem(R.id.menu_context_copy).setVisible(menuState.shouldShowCopyAction());
menu.findItem(R.id.menu_context_delete_message).setVisible(menuState.shouldShowDeleteAction());
List<ActionItem> items = new ArrayList<>();
AdaptiveActionsToolbar.adjustMenuActions(menu, 10, requireActivity().getWindow().getDecorView().getMeasuredWidth());
if (menuState.shouldShowReplyAction()) {
items.add(new ActionItem(R.drawable.ic_reply_24_tinted, getResources().getString(R.string.conversation_context__menu_reply_to_message), () -> {
maybeShowSwipeToReplyTooltip();
handleReplyMessage(getSelectedConversationMessage());
actionMode.finish();
}));
}
if (menuState.shouldShowForwardAction()) {
items.add(new ActionItem(R.drawable.ic_forward_24_tinted, getResources().getString(R.string.conversation_context__menu_forward_message), () -> handleForwardMessageParts(selectedParts)));
}
if (menuState.shouldShowSaveAttachmentAction()) {
items.add(new ActionItem(R.drawable.ic_save_24, getResources().getString(R.string.conversation_context_image__save_attachment), () -> {
handleSaveAttachment((MediaMmsMessageRecord) getSelectedConversationMessage().getMessageRecord());
}));
}
if (menuState.shouldShowCopyAction()) {
items.add(new ActionItem(R.drawable.ic_copy_24_tinted, getResources().getString(R.string.conversation_context__menu_copy_text), () -> {
handleCopyMessage(selectedParts);
}));
}
if (menuState.shouldShowDetailsAction()) {
items.add(new ActionItem(R.drawable.ic_info_tinted_24, getResources().getString(R.string.conversation_context__menu_message_details), () -> {
handleDisplayDetails(getSelectedConversationMessage());
}));
}
if (menuState.shouldShowDeleteAction()) {
items.add(new ActionItem(R.drawable.ic_delete_tinted_24, getResources().getString(R.string.conversation_context__menu_delete_message), () -> handleDeleteMessages(selectedParts)));
}
bottomActionBar.setItems(items);
}
private void setBottomActionBarVisibility(boolean isVisible) {
int visibility = isVisible ? View.VISIBLE : View.GONE;
bottomActionBar.setVisibility(visibility);
listener.onBottomActionBarVisibilityChanged(visibility);
int bottomPadding = isVisible ? (int) DimensionUnit.DP.toPixels(84) : getResources().getDimensionPixelSize(R.dimen.conversation_bottom_padding);
list.setPadding(list.getPaddingLeft(), list.getPaddingTop(), list.getPaddingRight(), bottomPadding);
}
private @Nullable ConversationAdapter getListAdapter() {
@@ -769,9 +814,12 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
}
private ConversationMessage getSelectedConversationMessage() {
Set<MultiselectPart> messageRecords = getListAdapter().getSelectedItems();
Set<ConversationMessage> messageRecords = Stream.of(getListAdapter().getSelectedItems())
.map(MultiselectPart::getConversationMessage)
.distinct()
.collect(Collectors.toSet());
if (messageRecords.size() == 1) return messageRecords.stream().findFirst().get().getConversationMessage();
if (messageRecords.size() == 1) return messageRecords.stream().findFirst().get();
else throw new AssertionError();
}
@@ -1130,11 +1178,9 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
if (!TextSecurePreferences.hasSeenSwipeToReplyTooltip(requireContext())) {
int text = ViewUtil.isLtr(requireContext()) ? R.string.ConversationFragment_you_can_swipe_to_the_right_reply
: R.string.ConversationFragment_you_can_swipe_to_the_left_reply;
TooltipPopup.forTarget(requireActivity().findViewById(R.id.menu_context_reply))
.setText(text)
.setTextColor(getResources().getColor(R.color.core_white))
.setBackgroundTint(getResources().getColor(R.color.core_ultramarine))
.show(TooltipPopup.POSITION_BELOW);
Snackbar.make(list, text, Snackbar.LENGTH_LONG)
.setTextColor(Color.WHITE)
.show();
TextSecurePreferences.setHasSeenSwipeToReplyTooltip(requireContext(), true);
}
@@ -1204,15 +1250,17 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
private @NonNull String calculateSelectedItemCount() {
ConversationAdapter adapter = getListAdapter();
if (adapter == null || adapter.getSelectedItems().isEmpty()) {
return String.valueOf(0);
int count = 0;
if (adapter != null && !adapter.getSelectedItems().isEmpty()) {
count = (int) adapter.getSelectedItems()
.stream()
.map(MultiselectPart::getConversationMessage)
.distinct()
.count();
}
return String.valueOf(adapter.getSelectedItems()
.stream()
.map(MultiselectPart::getConversationMessage)
.distinct()
.count());
return requireContext().getResources().getQuantityString(R.plurals.conversation_context__s_selected, count, count);
}
@Override
@@ -1227,6 +1275,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
void setThreadId(long threadId);
void handleReplyMessage(ConversationMessage conversationMessage);
void onMessageActionToolbarOpened();
void onBottomActionBarVisibilityChanged(int visibility);
void onForwardClicked();
void onMessageRequest(@NonNull MessageRequestViewModel viewModel);
void handleReaction(@NonNull ConversationMessage conversationMessage,
@@ -1322,7 +1371,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
if (getListAdapter().getSelectedItems().size() == 0) {
actionMode.finish();
} else {
setCorrectActionModeMenuVisibility(actionMode.getMenu());
setCorrectActionModeMenuVisibility();
actionMode.setTitle(calculateSelectedItemCount());
}
}
@@ -1806,12 +1855,9 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.conversation_context, menu);
mode.setTitle(calculateSelectedItemCount());
setCorrectActionModeMenuVisibility(menu);
setCorrectActionModeMenuVisibility();
listener.onMessageActionToolbarOpened();
return true;
}
@@ -1825,44 +1871,12 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
public void onDestroyActionMode(ActionMode mode) {
((ConversationAdapter)list.getAdapter()).clearSelection();
list.invalidateItemDecorations();
setBottomActionBarVisibility(false);
actionMode = null;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (actionMode == null) return false;
switch(item.getItemId()) {
case R.id.menu_context_copy:
handleCopyMessage(getListAdapter().getSelectedItems());
actionMode.finish();
return true;
case R.id.menu_context_delete_message:
handleDeleteMessages(getListAdapter().getSelectedItems());
actionMode.finish();
return true;
case R.id.menu_context_details:
handleDisplayDetails(getSelectedConversationMessage());
actionMode.finish();
return true;
case R.id.menu_context_forward:
handleForwardMessageParts(getListAdapter().getSelectedItems());
return true;
case R.id.menu_context_resend:
handleResendMessage(getSelectedConversationMessage().getMessageRecord());
actionMode.finish();
return true;
case R.id.menu_context_save_attachment:
handleSaveAttachment((MediaMmsMessageRecord) getSelectedConversationMessage().getMessageRecord());
actionMode.finish();
return true;
case R.id.menu_context_reply:
maybeShowSwipeToReplyTooltip();
handleReplyMessage(getSelectedConversationMessage());
actionMode.finish();
return true;
}
return false;
}
}

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@color/signal_icon_tint_primary" android:pathData="M9.5,5.621v3.7l1.309,0.169a10.932,10.932 0,0 1,9.364 7.253c0.161,0.406 0.8,1.756 0.8,1.756A19.408,19.408 0,0 0,19.5 17.2,17.455 17.455,0 0,0 11.115,14.5L9.5,14.38v4L3.121,12 9.5,5.621m1.137,-3.037a0.758,0.758 0,0 0,-0.491 0.27L1.354,11.646a0.5,0.5 0,0 0,0 0.708l8.792,8.792a0.758,0.758 0,0 0,0.491 0.27c0.219,0 0.363,-0.217 0.363,-0.623V16a14.706,14.706 0,0 1,10.905 5.426c0.282,0.355 0.514,0.529 0.677,0.529 0.214,0 0.3,-0.3 0.222,-0.9C21.822,14.051 18.264,8.934 11,8V3.207c0,-0.406 -0.144,-0.623 -0.363,-0.623Z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/signal_icon_tint_primary"
android:pathData="M12.91,3.91h0m1.71,2.16V9.32l-1.31,0.17A11,11 0,0 0,3.84 17c-0.12,0.33 -0.8,1.59 -0.8,1.59s1,-1 1.3,-1.22A17.36,17.36 0,0 1,13 14.5l1.62,-0.12v3.48l-0.06,0.81L15,18 21,12 14.9,5.9l-0.34,-0.54ZM13.48,2.58a0.76,0.76 0,0 1,0.49 0.27l8.79,8.8a0.48,0.48 0,0 1,0 0.7L14,21.15a0.76,0.76 0,0 1,-0.49 0.27c-0.22,0 -0.36,-0.22 -0.36,-0.63V16c-5,0.39 -8.83,2.48 -11.37,6 -0.14,0.2 -0.27,0.29 -0.37,0.29s-0.2,-0.17 -0.16,-0.5C2,14.43 5.59,9 13.12,8V3.21c0,-0.41 0.14,-0.63 0.36,-0.63Z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M16.38,4.5C16.1558,3.5058 15.6,2.6174 14.804,1.981C14.008,1.3445 13.0192,0.9978 12,0.9978C10.9808,0.9978 9.992,1.3445 9.196,1.981C8.4,2.6174 7.8442,3.5058 7.62,4.5H2V6H3.5L4.86,20C4.9216,20.5507 5.1842,21.0593 5.5975,21.4284C6.0109,21.7974 6.5459,22.001 7.1,22H16.9C17.4541,22.001 17.9891,21.7974 18.4025,21.4284C18.8158,21.0593 19.0784,20.5507 19.14,20L20.5,6H22V4.5H16.38ZM12,2.5C12.6189,2.5017 13.2222,2.6949 13.7271,3.0529C14.2319,3.411 14.6137,3.9165 14.82,4.5H9.18C9.3863,3.9165 9.7681,3.411 10.2729,3.0529C10.7778,2.6949 11.3811,2.5017 12,2.5V2.5ZM8,18L7.5,8H9L9.5,18H8ZM12.75,18H11.25V8H12.75V18ZM16,18H14.5L15,8H16.5L16,18Z"
android:fillColor="@color/signal_icon_tint_primary"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M22,4.5H16.35C16.1356,3.5056 15.5869,2.6147 14.7954,1.9756C14.0038,1.3366 13.0173,0.9881 12,0.9881C10.9827,0.9881 9.9962,1.3366 9.2046,1.9756C8.4131,2.6147 7.8644,3.5056 7.65,4.5H2V6H3.5L4.86,20C4.9216,20.5507 5.1842,21.0593 5.5975,21.4284C6.0109,21.7974 6.5459,22.001 7.1,22H16.9C17.4541,22.001 17.9891,21.7974 18.4025,21.4284C18.8158,21.0593 19.0784,20.5507 19.14,20L20.5,6H22V4.5ZM12,2.5C12.6189,2.5017 13.2222,2.6949 13.7271,3.0529C14.2319,3.411 14.6137,3.9165 14.82,4.5H9.18C9.3863,3.9165 9.7681,3.411 10.2729,3.0529C10.7778,2.6949 11.3811,2.5017 12,2.5V2.5ZM17.65,19.83C17.6281,20.0139 17.5398,20.1834 17.4017,20.3068C17.2636,20.4302 17.0852,20.4989 16.9,20.5H7.1C6.9148,20.4989 6.7364,20.4302 6.5983,20.3068C6.4602,20.1834 6.3719,20.0139 6.35,19.83L5,6H19L17.65,19.83ZM11.25,18V8H12.75V18H11.25ZM14.5,18L15,8H16.5L16,18H14.5ZM8,18L7.5,8H9L9.5,18H8Z"
android:fillColor="@color/signal_icon_tint_primary"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:autoMirrored="true"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/signal_icon_tint_primary"
android:pathData="M12.91,3.91h0m1.71,2.16V9.32l-1.31,0.17A11,11 0,0 0,3.84 17c-0.12,0.33 -0.8,1.59 -0.8,1.59s1,-1 1.3,-1.22A17.36,17.36 0,0 1,13 14.5l1.62,-0.12v3.48l-0.06,0.81L15,18 21,12 14.9,5.9l-0.34,-0.54ZM13.48,2.58a0.76,0.76 0,0 1,0.49 0.27l8.79,8.8a0.48,0.48 0,0 1,0 0.7L14,21.15a0.76,0.76 0,0 1,-0.49 0.27c-0.22,0 -0.36,-0.22 -0.36,-0.63V16c-5,0.39 -8.83,2.48 -11.37,6 -0.14,0.2 -0.27,0.29 -0.37,0.29s-0.2,-0.17 -0.16,-0.5C2,14.43 5.59,9 13.12,8V3.21c0,-0.41 0.14,-0.63 0.36,-0.63Z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@color/signal_icon_tint_primary" android:pathData="M9.5,5.621v3.7l1.309,0.169a10.932,10.932 0,0 1,9.364 7.253c0.161,0.406 0.8,1.756 0.8,1.756A19.408,19.408 0,0 0,19.5 17.2,17.455 17.455,0 0,0 11.115,14.5L9.5,14.38v4L3.121,12 9.5,5.621m1.137,-3.037a0.758,0.758 0,0 0,-0.491 0.27L1.354,11.646a0.5,0.5 0,0 0,0 0.708l8.792,8.792a0.758,0.758 0,0 0,0.491 0.27c0.219,0 0.363,-0.217 0.363,-0.623V16a14.706,14.706 0,0 1,10.905 5.426c0.282,0.355 0.514,0.529 0.677,0.529 0.214,0 0.3,-0.3 0.222,-0.9C21.822,14.051 18.264,8.934 11,8V3.207c0,-0.406 -0.144,-0.623 -0.363,-0.623Z"/>
</vector>

View File

@@ -49,7 +49,7 @@
android:layout_height="0dp"
android:layout_weight="1">
<FrameLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/fragment_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@@ -22,7 +22,7 @@
android:cacheColorHint="@color/signal_background_primary"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingBottom="2dp"
android:paddingBottom="@dimen/conversation_bottom_padding"
android:scrollbars="vertical"
android:overScrollMode="ifContentScrolls"
app:layout_constraintTop_toTopOf="parent" />
@@ -88,4 +88,15 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<org.thoughtcrime.securesms.components.menu.SignalBottomActionBar
android:id="@+id/conversation_bottom_action_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="16dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,46 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:title="@string/conversation_context__menu_message_details"
android:id="@+id/menu_context_details"
android:icon="@drawable/ic_info_white_24"
app:iconTint="@color/signal_icon_tint_primary"
app:showAsAction="always" />
<item android:title="@string/conversation_context__menu_delete_message"
android:id="@+id/menu_context_delete_message"
android:icon="@drawable/ic_trash_24"
app:iconTint="@color/signal_icon_tint_primary"
app:showAsAction="always" />
<item android:title="@string/conversation_context__menu_copy_text"
android:id="@+id/menu_context_copy"
android:icon="@drawable/ic_copy_24"
app:iconTint="@color/signal_icon_tint_primary"
app:showAsAction="always" />
<item android:title="@string/conversation_context__menu_reply_to_message"
android:id="@+id/menu_context_reply"
android:visible="true"
android:icon="@drawable/ic_reply_24"
app:iconTint="@color/signal_icon_tint_primary"
app:showAsAction="always" />
<item android:title="@string/conversation_context__menu_resend_message"
android:id="@+id/menu_context_resend"
android:visible="false"
app:showAsAction="never" />
<item android:title="@string/conversation_context_image__save_attachment"
android:id="@+id/menu_context_save_attachment"
android:visible="false"
android:icon="@drawable/ic_save_24"
app:iconTint="@color/signal_icon_tint_primary"
app:showAsAction="always" />
<item android:title="@string/conversation_context__menu_forward_message"
android:id="@+id/menu_context_forward"
android:icon="@drawable/ic_forward_24"
app:iconTint="@color/signal_icon_tint_primary"
app:showAsAction="always" />
</menu>

View File

@@ -69,6 +69,7 @@
<dimen name="thumbnail_default_radius">4dp</dimen>
<dimen name="conversation_bottom_padding">2dp</dimen>
<dimen name="conversation_compose_height">40dp</dimen>
<dimen name="conversation_individual_right_gutter">16dp</dimen>
<dimen name="conversation_individual_left_gutter">16dp</dimen>

View File

@@ -2775,18 +2775,32 @@
<string name="conversation_callable_secure__menu_video">Signal video call</string>
<!-- conversation_context -->
<string name="conversation_context__menu_message_details">Message details</string>
<string name="conversation_context__menu_copy_text">Copy text</string>
<string name="conversation_context__menu_delete_message">Delete message</string>
<string name="conversation_context__menu_forward_message">Forward message</string>
<!-- Button to view detailed information for a message -->
<string name="conversation_context__menu_message_details">Info</string>
<!-- Button to copy a message's text to the clipboard -->
<string name="conversation_context__menu_copy_text">Copy</string>
<!-- Button to delete a message -->
<string name="conversation_context__menu_delete_message">Delete</string>
<!-- Button to forward a message to another person or group chat -->
<string name="conversation_context__menu_forward_message">Forward</string>
<!-- Button to retry sending a message -->
<string name="conversation_context__menu_resend_message">Resend message</string>
<string name="conversation_context__menu_reply_to_message">Reply to message</string>
<!-- Button to reply to a message -->
<string name="conversation_context__menu_reply_to_message">Reply</string>
<!-- conversation_context_reacction -->
<!-- conversation_context_reaction -->
<!-- Button to select a message and enter selection mode -->
<string name="conversation_context__reaction_multi_select">Select multiple</string>
<!-- Heading which shows how many messages are currently selected -->
<plurals name="conversation_context__s_selected">
<item quantity="one">%d selected</item>
<item quantity="other">%d selected</item>
</plurals>
<!-- conversation_context_image -->
<string name="conversation_context_image__save_attachment">Save attachment</string>
<!-- Button to save a message attachment (image, file etc.) -->
<string name="conversation_context_image__save_attachment">Save</string>
<!-- conversation_expiring_off -->
<string name="conversation_expiring_off__disappearing_messages">Disappearing messages</string>