From f4a769080b70571a2805ea53d712bb350cfacb63 Mon Sep 17 00:00:00 2001 From: Mishiranu Date: Fri, 25 Nov 2016 17:06:43 +0300 Subject: [PATCH] Add quotation support --- .../ui/ConversationFragment.java | 29 +++ .../ui/adapter/MessageAdapter.java | 178 +++++++++++++++++- .../conversations/ui/text/DividerSpan.java | 29 +++ .../conversations/ui/text/QuoteSpan.java | 52 +++++ .../ui/widget/ListSelectionManager.java | 20 +- .../res/drawable-hdpi/ic_action_reply.png | Bin 0 -> 462 bytes .../res/drawable-hdpi/ic_reply_white_24dp.png | Bin 0 -> 253 bytes .../res/drawable-mdpi/ic_action_reply.png | Bin 0 -> 343 bytes .../res/drawable-mdpi/ic_reply_white_24dp.png | Bin 0 -> 186 bytes .../res/drawable-xhdpi/ic_action_reply.png | Bin 0 -> 561 bytes .../drawable-xhdpi/ic_reply_white_24dp.png | Bin 0 -> 306 bytes .../res/drawable-xxhdpi/ic_action_reply.png | Bin 0 -> 775 bytes .../drawable-xxhdpi/ic_reply_white_24dp.png | Bin 0 -> 436 bytes .../drawable-xxxhdpi/ic_reply_white_24dp.png | Bin 0 -> 579 bytes src/main/res/values-ru/strings.xml | 1 + src/main/res/values-v21/themes.xml | 2 + src/main/res/values/attrs.xml | 1 + src/main/res/values/colors.xml | 1 + src/main/res/values/strings.xml | 1 + src/main/res/values/themes.xml | 2 + 20 files changed, 305 insertions(+), 11 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/ui/text/DividerSpan.java create mode 100644 src/main/java/eu/siacs/conversations/ui/text/QuoteSpan.java create mode 100644 src/main/res/drawable-hdpi/ic_action_reply.png create mode 100644 src/main/res/drawable-hdpi/ic_reply_white_24dp.png create mode 100644 src/main/res/drawable-mdpi/ic_action_reply.png create mode 100644 src/main/res/drawable-mdpi/ic_reply_white_24dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_action_reply.png create mode 100644 src/main/res/drawable-xhdpi/ic_reply_white_24dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_action_reply.png create mode 100644 src/main/res/drawable-xxhdpi/ic_reply_white_24dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_reply_white_24dp.png diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 271aa26c..54b60949 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -11,6 +11,7 @@ import android.content.Intent; import android.content.IntentSender.SendIntentException; import android.os.Bundle; import android.os.Handler; +import android.text.Editable; import android.text.InputType; import android.util.Log; import android.util.Pair; @@ -506,6 +507,34 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } } }); + messageListAdapter.setOnQuoteListener(new MessageAdapter.OnQuoteListener() { + + @Override + public void onQuote(String text) { + if (mEditMessage.isEnabled()) { + text = text.replaceAll("(\n *){2,}", "\n").replaceAll("(^|\n)", "$1> ").replaceAll("\n$", ""); + Editable editable = mEditMessage.getEditableText(); + int position = mEditMessage.getSelectionEnd(); + if (position == -1) position = editable.length(); + if (position > 0 && editable.charAt(position - 1) != '\n') { + editable.insert(position++, "\n"); + } + editable.insert(position, text); + position += text.length(); + editable.insert(position++, "\n"); + if (position < editable.length() && editable.charAt(position) != '\n') { + editable.insert(position, "\n"); + } + mEditMessage.setSelection(position); + mEditMessage.requestFocus(); + InputMethodManager inputMethodManager = (InputMethodManager) getActivity() + .getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputMethodManager != null) { + inputMethodManager.showSoftInput(mEditMessage, InputMethodManager.SHOW_IMPLICIT); + } + } + } + }); messagesView.setAdapter(messageListAdapter); registerForContextMenu(messagesView); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index be885142..6a392ee1 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -5,6 +5,7 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; @@ -23,6 +24,9 @@ import android.text.style.StyleSpan; import android.text.util.Linkify; import android.util.DisplayMetrics; import android.util.Patterns; +import android.view.ActionMode; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; @@ -38,8 +42,6 @@ import java.lang.ref.WeakReference; import java.net.URL; import java.util.List; import java.util.concurrent.RejectedExecutionException; -import java.util.regex.MatchResult; -import java.util.regex.Matcher; import java.util.regex.Pattern; import eu.siacs.conversations.Config; @@ -54,6 +56,8 @@ import eu.siacs.conversations.entities.Message.FileParams; import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.ui.ConversationActivity; +import eu.siacs.conversations.ui.text.DividerSpan; +import eu.siacs.conversations.ui.text.QuoteSpan; import eu.siacs.conversations.ui.widget.ClickableMovementMethod; import eu.siacs.conversations.ui.widget.CopyTextView; import eu.siacs.conversations.ui.widget.ListSelectionManager; @@ -82,6 +86,8 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie private boolean mIndicateReceived = false; private boolean mUseGreenBackground = false; + private OnQuoteListener onQuoteListener; + private final ListSelectionManager listSelectionManager = new ListSelectionManager(); public MessageAdapter(ConversationActivity activity, List messages) { @@ -100,6 +106,10 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie this.mOnContactPictureLongClickedListener = listener; } + public void setOnQuoteListener(OnQuoteListener listener) { + this.onQuoteListener = listener; + } + @Override public int getViewTypeCount() { return 3; @@ -292,10 +302,78 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie viewHolder.messageBody.setIncludeFontPadding(false); Spannable span = new SpannableString(body); span.setSpan(new RelativeSizeSpan(4.0f), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - span.setSpan(new ForegroundColorSpan(activity.getWarningTextColor()), 0, body.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + span.setSpan(new ForegroundColorSpan(activity.getWarningTextColor()), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); viewHolder.messageBody.setText(span); } + private int applyQuoteSpan(SpannableStringBuilder body, int start, int end, boolean darkBackground) { + if (start > 1 && !"\n\n".equals(body.subSequence(start - 2, start).toString())) { + body.insert(start++, "\n"); + body.setSpan(new DividerSpan(false), start - 2, start, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + end++; + } + if (end < body.length() - 1 && !"\n\n".equals(body.subSequence(end, end + 2).toString())) { + body.insert(end, "\n"); + body.setSpan(new DividerSpan(false), end, end + 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + int color = darkBackground ? this.getMessageTextColor(darkBackground, false) + : getContext().getResources().getColor(R.color.bubble); + DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); + body.setSpan(new QuoteSpan(color, metrics), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + return 0; + } + + /** + * Applies QuoteSpan to group of lines which starts with > or » characters. + * Appends likebreaks and applies DividerSpan to them to show a padding between quote and text. + */ + private boolean handleTextQuotes(SpannableStringBuilder body, boolean darkBackground) { + boolean startsWithQuote = false; + char previous = '\n'; + int lineStart = -1; + int lineTextStart = -1; + int quoteStart = -1; + for (int i = 0; i <= body.length(); i++) { + char current = body.length() > i ? body.charAt(i) : '\n'; + if (lineStart == -1) { + if (previous == '\n') { + if (current == '>' || current == '\u00bb') { + // Line start with quote + lineStart = i; + if (quoteStart == -1) quoteStart = i; + if (i == 0) startsWithQuote = true; + } else if (quoteStart >= 0) { + // Line start without quote, apply spans there + applyQuoteSpan(body, quoteStart, i - 1, darkBackground); + quoteStart = -1; + } + } + } else { + // Remove extra spaces between > and first character in the line + // > character will be removed too + if (current != ' ' && lineTextStart == -1) { + lineTextStart = i; + } + if (current == '\n') { + body.delete(lineStart, lineTextStart); + i -= lineTextStart - lineStart; + if (i == lineStart) { + // Avoid empty lines because span over empty line can be hidden + body.insert(i++, " "); + } + lineStart = -1; + lineTextStart = -1; + } + } + previous = current; + } + if (quoteStart >= 0) { + // Apply spans to finishing open quote + applyQuoteSpan(body, quoteStart, body.length(), darkBackground); + } + return startsWithQuote; + } + private void displayTextMessage(final ViewHolder viewHolder, final Message message, boolean darkBackground, int type) { if (viewHolder.download_button != null) { viewHolder.download_button.setVisibility(View.GONE); @@ -318,8 +396,9 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie for (Message.MergeSeparator mergeSeparator : mergeSeparators) { int start = body.getSpanStart(mergeSeparator); int end = body.getSpanEnd(mergeSeparator); - body.setSpan(new RelativeSizeSpan(0.3f), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + body.setSpan(new DividerSpan(true), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } + boolean startsWithQuote = handleTextQuotes(body, darkBackground); if (message.getType() != Message.TYPE_PRIVATE) { if (hasMeCommand) { body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, nick.length(), @@ -340,7 +419,13 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie } body.insert(0, privateMarker); int privateMarkerIndex = privateMarker.length(); - body.insert(privateMarkerIndex, " "); + if (startsWithQuote) { + body.insert(privateMarkerIndex, "\n\n"); + body.setSpan(new DividerSpan(false), privateMarkerIndex, privateMarkerIndex + 2, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } else { + body.insert(privateMarkerIndex, " "); + } body.setSpan(new ForegroundColorSpan(getMessageTextColor(darkBackground, false)), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); body.setSpan(new StyleSpan(Typeface.BOLD), @@ -364,7 +449,8 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie } viewHolder.messageBody.setTextColor(this.getMessageTextColor(darkBackground, true)); viewHolder.messageBody.setLinkTextColor(this.getMessageTextColor(darkBackground, true)); - viewHolder.messageBody.setHighlightColor(activity.getResources().getColor(darkBackground ? (type == SENT || !mUseGreenBackground ? R.color.black26 : R.color.grey800) : R.color.grey500)); + viewHolder.messageBody.setHighlightColor(activity.getResources().getColor(darkBackground + ? (type == SENT || !mUseGreenBackground ? R.color.black26 : R.color.grey800) : R.color.grey500)); viewHolder.messageBody.setTypeface(null, Typeface.NORMAL); } @@ -527,7 +613,8 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie break; } if (viewHolder.messageBody != null) { - listSelectionManager.onCreate(viewHolder.messageBody); + listSelectionManager.onCreate(viewHolder.messageBody, + new MessageBodyActionModeCallback(viewHolder.messageBody)); viewHolder.messageBody.setCopyHandler(this); } view.setTag(viewHolder); @@ -687,9 +774,84 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie listSelectionManager.onAfterNotifyDataSetChanged(); } + private String transformText(CharSequence text, int start, int end, boolean forCopy) { + SpannableStringBuilder builder = new SpannableStringBuilder(text); + Object copySpan = new Object(); + builder.setSpan(copySpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + DividerSpan[] dividerSpans = builder.getSpans(0, builder.length(), DividerSpan.class); + for (DividerSpan dividerSpan : dividerSpans) { + builder.replace(builder.getSpanStart(dividerSpan), builder.getSpanEnd(dividerSpan), + dividerSpan.isLarge() ? "\n\n" : "\n"); + } + start = builder.getSpanStart(copySpan); + end = builder.getSpanEnd(copySpan); + if (start == -1 || end == -1) return ""; + builder = new SpannableStringBuilder(builder, start, end); + if (forCopy) { + QuoteSpan[] quoteSpans = builder.getSpans(0, builder.length(), QuoteSpan.class); + for (QuoteSpan quoteSpan : quoteSpans) { + builder.insert(builder.getSpanStart(quoteSpan), "> "); + } + } + return builder.toString(); + } + @Override public String transformTextForCopy(CharSequence text, int start, int end) { - return text.toString().substring(start, end); + if (text instanceof Spanned) { + return transformText(text, start, end, true); + } else { + return text.toString().substring(start, end); + } + } + + public interface OnQuoteListener { + public void onQuote(String text); + } + + private class MessageBodyActionModeCallback implements ActionMode.Callback { + + private final TextView textView; + + public MessageBodyActionModeCallback(TextView textView) { + this.textView = textView; + } + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + if (onQuoteListener != null) { + int quoteResId = activity.getThemeResource(R.attr.icon_quote, R.drawable.ic_action_reply); + // 3rd item is placed after "copy" item + menu.add(0, android.R.id.button1, 3, R.string.quote).setIcon(quoteResId) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + return false; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + if (item.getItemId() == android.R.id.button1) { + int start = textView.getSelectionStart(); + int end = textView.getSelectionEnd(); + if (end > start) { + String text = transformText(textView.getText(), start, end, false); + if (onQuoteListener != null) { + onQuoteListener.onQuote(text); + } + mode.finish(); + } + return true; + } + return false; + } + + @Override + public void onDestroyActionMode(ActionMode mode) {} } public void openDownloadable(Message message) { diff --git a/src/main/java/eu/siacs/conversations/ui/text/DividerSpan.java b/src/main/java/eu/siacs/conversations/ui/text/DividerSpan.java new file mode 100644 index 00000000..234b3300 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/ui/text/DividerSpan.java @@ -0,0 +1,29 @@ +package eu.siacs.conversations.ui.text; + +import android.text.TextPaint; +import android.text.style.MetricAffectingSpan; + +public class DividerSpan extends MetricAffectingSpan { + + private static final float PROPORTION = 0.3f; + + private final boolean large; + + public DividerSpan(boolean large) { + this.large = large; + } + + public boolean isLarge() { + return large; + } + + @Override + public void updateDrawState(TextPaint tp) { + tp.setTextSize(tp.getTextSize() * PROPORTION); + } + + @Override + public void updateMeasureState(TextPaint p) { + p.setTextSize(p.getTextSize() * PROPORTION); + } +} \ No newline at end of file diff --git a/src/main/java/eu/siacs/conversations/ui/text/QuoteSpan.java b/src/main/java/eu/siacs/conversations/ui/text/QuoteSpan.java new file mode 100644 index 00000000..272d794e --- /dev/null +++ b/src/main/java/eu/siacs/conversations/ui/text/QuoteSpan.java @@ -0,0 +1,52 @@ +package eu.siacs.conversations.ui.text; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.text.Layout; +import android.text.TextPaint; +import android.text.style.CharacterStyle; +import android.text.style.LeadingMarginSpan; +import android.util.DisplayMetrics; +import android.util.TypedValue; + +public class QuoteSpan extends CharacterStyle implements LeadingMarginSpan { + + private final int color; + + private final int width; + private final int paddingLeft; + private final int paddingRight; + + private static final float WIDTH_SP = 2f; + private static final float PADDING_LEFT_SP = 1.5f; + private static final float PADDING_RIGHT_SP = 8f; + + public QuoteSpan(int color, DisplayMetrics metrics) { + this.color = color; + this.width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, WIDTH_SP, metrics); + this.paddingLeft = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, PADDING_LEFT_SP, metrics); + this.paddingRight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, PADDING_RIGHT_SP, metrics); + } + + @Override + public void updateDrawState(TextPaint tp) { + tp.setColor(this.color); + } + + @Override + public int getLeadingMargin(boolean first) { + return paddingLeft + width + paddingRight; + } + + @Override + public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, + CharSequence text, int start, int end, boolean first, Layout layout) { + Paint.Style style = p.getStyle(); + int color = p.getColor(); + p.setStyle(Paint.Style.FILL); + p.setColor(this.color); + c.drawRect(x + dir * paddingLeft, top, x + dir * (paddingLeft + width), bottom, p); + p.setStyle(style); + p.setColor(color); + } +} \ No newline at end of file diff --git a/src/main/java/eu/siacs/conversations/ui/widget/ListSelectionManager.java b/src/main/java/eu/siacs/conversations/ui/widget/ListSelectionManager.java index 9e256448..4be90712 100644 --- a/src/main/java/eu/siacs/conversations/ui/widget/ListSelectionManager.java +++ b/src/main/java/eu/siacs/conversations/ui/widget/ListSelectionManager.java @@ -69,8 +69,8 @@ public class ListSelectionManager { private int futureSelectionStart; private int futureSelectionEnd; - public void onCreate(TextView textView) { - final CustomCallback callback = new CustomCallback(textView); + public void onCreate(TextView textView, ActionMode.Callback additionalCallback) { + final CustomCallback callback = new CustomCallback(textView, additionalCallback); textView.setCustomSelectionActionModeCallback(callback); } @@ -112,10 +112,12 @@ public class ListSelectionManager { private class CustomCallback implements ActionMode.Callback { private final TextView textView; + private final ActionMode.Callback additionalCallback; public Object identifier; - public CustomCallback(TextView textView) { + public CustomCallback(TextView textView, ActionMode.Callback additionalCallback) { this.textView = textView; + this.additionalCallback = additionalCallback; } @Override @@ -123,21 +125,33 @@ public class ListSelectionManager { selectionActionMode = mode; selectionIdentifier = identifier; selectionTextView = textView; + if (additionalCallback != null) { + additionalCallback.onCreateActionMode(mode, menu); + } return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + if (additionalCallback != null) { + additionalCallback.onPrepareActionMode(mode, menu); + } return true; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + if (additionalCallback != null && additionalCallback.onActionItemClicked(mode, item)) { + return true; + } return false; } @Override public void onDestroyActionMode(ActionMode mode) { + if (additionalCallback != null) { + additionalCallback.onDestroyActionMode(mode); + } if (selectionActionMode == mode) { selectionActionMode = null; selectionIdentifier = null; diff --git a/src/main/res/drawable-hdpi/ic_action_reply.png b/src/main/res/drawable-hdpi/ic_action_reply.png new file mode 100644 index 0000000000000000000000000000000000000000..b3bae928957e22b4127367b9b0be667752df35de GIT binary patch literal 462 zcmV;<0WtoGP)fGKoCV)%fA$m6hH%M zfC5YdDIjhj|8~kS1=t19fE#cFE&vLk0CGpd5+8}p<1Vu+%}67J#KJwyYhjZ_ZW{wI z00S@p0|*kJ>$>-70U*A>;SeRjn&RmIYm27>Xo#l)Xo;r)Xo_D9pe=q8KnUVz1B4=e z7C=bi#kL;Br?~!OaNauS?rZ?3^yXW*z}(>1C15U)v=M+3AXhrT$~i9pKLBYdMXXS; zIsAm@rZIoeWK$rX0w9!?gj5OQK#71NM2;xrloV2g_@WImfxsI{CKUAzYxaYNkoKt| z{t15@lS){ZJ)0Agig-yRq&30?>H?SUR*qa+P`eN-op7IWBt%AUqhyHY)nM~72Z)7z z{;PiAdpzSCUek7?gu+h#5&JNS#!+g;%XZoagmf6JZ~Dz#<@I~OBSfM4TSar&Q8#lM z5L@;Q2kEp+Vz#J%ulJFtqXvkwhz-C148Q;;+phov0FBs}aEBSND*ylh07*qoM6N<$ Ef?-s=5C8xG literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-hdpi/ic_reply_white_24dp.png b/src/main/res/drawable-hdpi/ic_reply_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..0424c2bd6d6eaa5fcd3398f71b6e230ffc1caa7b GIT binary patch literal 253 zcmV zU~Fdwq%m1M4)uK)mzoQzo=rJT_^F%Fw{vzaQvoP=Gs*64?5z`yC5|0te ztglo_JlM#YTZ56ps3l~=R%Q4bDkjod4_PXH*Zy-CV1eOQf08V!00000NkvXXu0mjf DTghfP literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-mdpi/ic_action_reply.png b/src/main/res/drawable-mdpi/ic_action_reply.png new file mode 100644 index 0000000000000000000000000000000000000000..ce00dbc4b436cb7b84cfea033ad660f96bb306b9 GIT binary patch literal 343 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GKRsO>Ln>}1B}lL?PFT_O|NsBbO$JM!8BEN!oG^>?NLEQX zgQ>3iO~xzR=BTh=I8?Mq)9+9Xz?hgCKopV_qG57TJY(G)P zFtz5NOh#31XE=q(=M%&bP0l+XkK#q@oT literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-mdpi/ic_reply_white_24dp.png b/src/main/res/drawable-mdpi/ic_reply_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..862114b82dafbbf8ca125d02ce7b723e901cf396 GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_+iMo$;V5R22v2@MvKutno2LKY{^$HJeV;W&5y zd7zR&QKbLh*2~7ZpEmt@I literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/ic_action_reply.png b/src/main/res/drawable-xhdpi/ic_action_reply.png new file mode 100644 index 0000000000000000000000000000000000000000..31df111267643cb59e5a7649734e4225182af620 GIT binary patch literal 561 zcmV-10?z%3P)^q;yebT8?t2+HZJ9ca+3d1lA z!!QiPFr=90d4r|lx0ETu!^=BVwK@UX!ix&f7+y?(*6<<%G>2ywpglaRfE>cJ3CJZp zi-4TMM;DM=_^1MM3?EHEuHmBy$T@tlfZW3)E@Ai+)cDJ1U-a4JoEy{$NYL{=)EVj% zgp5fAKzIy&gnCFcYEecDB6@KF6drqC!)Xo*IE0P}mudJPCX#Sw6`(ZyB`Bd&dmtjf zD|FIsjkmxxwePaIcmz;=V>ft& zYwlEShPWhL^e(oKE5sAL_Yu-2+is9?B%COCOV=n=3gD6eZSG?P7dsCSm>6{n&@Msj zL`bUy(IYYKw}Tj({!!8}48t%C!!TCLw*UhG4C~4cD99Zp00000NkvXXu0mjf+?nRC literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/ic_reply_white_24dp.png b/src/main/res/drawable-xhdpi/ic_reply_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..885623e4d0bb82cbb39424905a689b9bc6af22c1 GIT binary patch literal 306 zcmV-20nPr2P) zzY0NN7>7?uDP=Jjlqgb0Zh^@tcYxg;Sd=SZ;sR_Ynd}Az17%Q>tQLPtJ%&RLN}ZhF zoX28{P=t|1-fkT|O<6C-?kx7!_{n(kXXy{Yr1VCN3 zwvc59OoWRUan?XgxH^(xM@_qC=3M_tt`m97U@*LJ14+y<45uXsEdT%j07*qoM6N<$ Ef@&9ni~s-t literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxhdpi/ic_action_reply.png b/src/main/res/drawable-xxhdpi/ic_action_reply.png new file mode 100644 index 0000000000000000000000000000000000000000..119006014d5e9c4b06670e88ed1f18633475172a GIT binary patch literal 775 zcmV+i1Ni)jP)X1-Jn> zKm+Cu+))F$hujHwTtHGlQb1Aw1)u_O0WQFN->?}nVF-$|(!Sd7d1iKW@Ql5BpZ$$i zX&L|k0000000000004l891e%qUPz)`_?K4JwcJy;r10(X&C05#M%58M0>XwbFQE4D zr3FL?Usgbr@FfLA3SUk@wD6?_L=0a>K-BOh1Vj$MxPa*47ZpJG1qBd(K>>tcP=JN+ zp5m!5(KnCTE5E|tn94( zNP@msDPUp4&aIU@zn1DTZQ$@9b7SRt?w-*S{$IF&a})7%F#;-G?QBiLR$f5mOHH4+ zPp9k+E#Z$^LG>nKkP;AVleJGhA^QZ~H82KmWM5LFV+bnT6F%mg6PSdLn3zq#2bm!# zO-_Ak9TD)qLw#Z+R-Sh`hN5hfaKN5$CbtR-CSjf9UA64X&|nfaGIPMpb3*^rrYIeK z@!x-wu5YcJD9#1#l+9y83H@_-rB)Z0@ZN`x%yqf2x?D-_gq4OPtBXH$@w!}OuFs2x zp>Bp7mzon&Yt31o)l1&o?`FE`dkH%c3Ar1^T3Wz^oAxdIASB^yrms7Z7m#jxpIV-Z zgzM(_BgkX%9(^u52`@o{aZGsowEEd3iOpgJs7dhE(KF^IulF{s{jYNTepm3um3qRM zVdy3Wzs-4us4Lw1R3zN~=Q9uf^Av}-I)NcTLBjBlUg10LNl!&cJQFT0;ZeZ56nsC& zDLI*k@&Y~Keo48R^LS$0h%xddzFy^Yb2U9cc%KkUKr{)m2eYC`kUkj~L4y3z#M&h+ z;%sQNiCLB!|8?KfTYNQj|)JTUktC6o+dq?9H~EB^BF0Ts(zKIf z5J4hr@>BtIUWa@1)q^5EM0qG9O*%bF6Jn|4P8~ZRHV|gNh&14Q97BLv9^V(QOAM?S z;xf1Rj-bRyk1kR<(zwXblU5P{#AL?8G_eO<2@TF^;k1r#T3s%un+$WFQVbIJv?AhJ emStJizv~TwJ`C-NfL~|;0000uF!KmdtKp{iiQvMxf>2_d&JP>CcpFGJ|%{xxY)28x!eL{dJk`Fa!>K&N3VGZ{N^_u4!fXoj~TUKm5B^COvaK6>~ z%nyx)*5T6*UK3%rJj%4a(q;C=>hs?|^@;_jS)7)=XXrKc6JL37pW>cJ=WA2LZytYr zJn|{`{`bq;S&y4c&oJ7}+1HqD(u#}siQU!g zF8R52*8bq;$a@AWj?2{_m$P1B{9@7Z;@Lf=b>GgsK6p8lVZr&h_3G*23=Nh0-Rnge zL^MjUnheIa3Vz$geeFfLe6(07KI|wm+6>nf*QD9)?aA05( dXkhrm;9=?%ov5mF5|}s`JYD@<);T3K0RT(&^4|ae literal 0 HcmV?d00001 diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index 215e0ad9..25bf34a8 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -332,6 +332,7 @@ Опции сообщения Копировать текст Выбрать текст + Цитировать Копировать адрес ссылки Отправить ещё раз URL файла diff --git a/src/main/res/values-v21/themes.xml b/src/main/res/values-v21/themes.xml index d6612552..7cac79c2 100644 --- a/src/main/res/values-v21/themes.xml +++ b/src/main/res/values-v21/themes.xml @@ -51,6 +51,7 @@ @drawable/ic_done_black_24dp @drawable/ic_group_white_24dp @drawable/ic_add_white_24dp + @drawable/ic_reply_white_24dp @drawable/ic_refresh_black_24dp @drawable/ic_attach_file_white_24dp @drawable/ic_lock_open_white_24dp @@ -117,6 +118,7 @@ @drawable/ic_done_black_24dp @drawable/ic_group_white_24dp @drawable/ic_add_white_24dp + @drawable/ic_reply_white_24dp @drawable/ic_refresh_white_24dp @drawable/ic_attach_file_white_24dp @drawable/ic_lock_open_white_24dp diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml index 82f9db89..e402901c 100644 --- a/src/main/res/values/attrs.xml +++ b/src/main/res/values/attrs.xml @@ -40,6 +40,7 @@ + diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml index 2d97bd41..60db84dd 100644 --- a/src/main/res/values/colors.xml +++ b/src/main/res/values/colors.xml @@ -21,4 +21,5 @@ #ffc62828 #ffff9800 #ff259b24 + #ff4b9b4a \ No newline at end of file diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index c12b5e96..7a528d77 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -364,6 +364,7 @@ Message options Copy text Select text + Quote Copy original URL Send again File URL diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml index f15822c9..46ea691b 100644 --- a/src/main/res/values/themes.xml +++ b/src/main/res/values/themes.xml @@ -50,6 +50,7 @@ @drawable/ic_action_new @drawable/ic_action_new_attachment @drawable/ic_action_not_secure + @drawable/ic_action_reply @drawable/ic_action_refresh @drawable/ic_action_remove @drawable/ic_action_search @@ -113,6 +114,7 @@ @drawable/ic_action_new @drawable/ic_action_new_attachment @drawable/ic_action_not_secure + @drawable/ic_action_reply @drawable/ic_action_refresh_white @drawable/ic_action_remove_white @drawable/ic_action_search