Implement bottom selection menu in chat.

fork-5.53.8
Rashad Sookram 2022-01-11 09:36:21 -05:00 zatwierdzone przez Cody Henthorne
rodzic 917744f091
commit 3943e670b2
15 zmienionych plików z 209 dodań i 122 usunięć

Wyświetl plik

@ -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);

Wyświetl plik

@ -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();

Wyświetl plik

@ -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;
@ -55,6 +55,7 @@ import androidx.core.app.ActivityCompat;
import androidx.core.app.ActivityOptionsCompat;
import androidx.core.text.HtmlCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.ViewKt;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
@ -65,17 +66,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.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.menu.ActionItem;
import org.thoughtcrime.securesms.components.menu.SignalBottomActionBar;
import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager;
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner;
@ -160,20 +163,23 @@ import org.thoughtcrime.securesms.util.TopToastPopup;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.WindowUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
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.verify.VerifyIdentityActivity;
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;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import kotlin.Unit;
@ -224,6 +230,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 +272,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(
@ -739,7 +747,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 +755,86 @@ 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());
actionMode.finish();
}));
}
if (menuState.shouldShowCopyAction()) {
items.add(new ActionItem(R.drawable.ic_copy_24_tinted, getResources().getString(R.string.conversation_context__menu_copy_text), () -> {
handleCopyMessage(selectedParts);
actionMode.finish();
}));
}
if (menuState.shouldShowDetailsAction()) {
items.add(new ActionItem(R.drawable.ic_info_tinted_24, getResources().getString(R.string.conversation_context__menu_message_details), () -> {
handleDisplayDetails(getSelectedConversationMessage());
actionMode.finish();
}));
}
if (menuState.shouldShowDeleteAction()) {
items.add(new ActionItem(R.drawable.ic_delete_tinted_24, getResources().getString(R.string.conversation_context__menu_delete_message), () -> {
handleDeleteMessages(selectedParts);
actionMode.finish();
}));
}
bottomActionBar.setItems(items);
}
private void setBottomActionBarVisibility(boolean isVisible) {
boolean isCurrentlyVisible = bottomActionBar.getVisibility() == View.VISIBLE;
if (isVisible == isCurrentlyVisible) {
return;
}
int scrollOffset = (int) DimensionUnit.DP.toPixels(34);
if (isVisible) {
ViewUtil.animateIn(bottomActionBar, bottomActionBar.getEnterAnimation());
listener.onBottomActionBarVisibilityChanged(View.VISIBLE);
list.setPadding(list.getPaddingLeft(), list.getPaddingTop(), list.getPaddingRight(), (int) DimensionUnit.DP.toPixels(88));
list.scrollBy(0, -scrollOffset);
} else {
ViewUtil.animateOut(bottomActionBar, bottomActionBar.getExitAnimation())
.addListener(new ListenableFuture.Listener<Boolean>() {
@Override public void onSuccess(Boolean result) {
listener.onBottomActionBarVisibilityChanged(View.GONE);
list.setPadding(list.getPaddingLeft(), list.getPaddingTop(), list.getPaddingRight(), getResources().getDimensionPixelSize(R.dimen.conversation_bottom_padding));
ViewKt.doOnPreDraw(list, view -> {
list.scrollBy(0, scrollOffset);
return Unit.INSTANCE;
});
}
@Override public void onFailure(ExecutionException e) {
}
});
}
}
private @Nullable ConversationAdapter getListAdapter() {
@ -769,9 +846,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 +1210,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 +1282,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 +1307,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 +1403,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 +1887,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 +1903,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;
}
}

Wyświetl plik

@ -108,8 +108,10 @@ class MultiselectItemDecoration(
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val currentSelection = getCurrentSelection(parent)
if (selectedParts.isEmpty() && currentSelection.isNotEmpty()) {
val wasRunning = enterExitAnimation?.isRunning ?: false
enterExitAnimation?.end()
enterExitAnimation = ValueAnimator.ofFloat(enterExitAnimation?.animatedFraction ?: 0f, 1f).apply {
val startValue = if (wasRunning) enterExitAnimation?.animatedFraction else 0f
enterExitAnimation = ValueAnimator.ofFloat(startValue ?: 0f, 1f).apply {
duration = 150L
start()
}
@ -142,7 +144,10 @@ class MultiselectItemDecoration(
if (adapter.selectedItems.isEmpty()) {
drawFocusShadeUnderIfNecessary(canvas, parent)
return
if (enterExitAnimation == null || !isInitialAnimation()) {
return
}
}
shadePaint.color = when {
@ -189,7 +194,9 @@ class MultiselectItemDecoration(
canvas.restore()
}
drawChecks(parent, canvas, adapter)
if (adapter.selectedItems.isNotEmpty()) {
drawChecks(parent, canvas, adapter)
}
}
/**
@ -312,7 +319,8 @@ class MultiselectItemDecoration(
val adapter = parent.adapter as ConversationAdapter
val isLtr = ViewUtil.isLtr(child)
if (adapter.selectedItems.isNotEmpty() && child is Multiselectable) {
val isAnimatingSelection = enterExitAnimation != null && isInitialAnimation()
if ((isAnimatingSelection || adapter.selectedItems.isNotEmpty()) && child is Multiselectable) {
val target = child.getHorizontalTranslationTarget()
if (target != null) {
@ -323,7 +331,7 @@ class MultiselectItemDecoration(
}
val translation: Float = if (isInitialAnimation()) {
max(0, gutter - start) * (enterExitAnimation?.animatedFraction ?: 1f)
max(0, gutter - start) * (enterExitAnimation?.animatedValue as Float? ?: 1f)
} else {
max(0, gutter - start).toFloat()
}

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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" />

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -2785,13 +2785,22 @@
<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>