mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 01:40:07 +01:00
Add text formatting send and receive support for conversations.
This commit is contained in:
committed by
Greyson Parrelli
parent
aa2075c78f
commit
cc490f4b73
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.components;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Annotation;
|
||||
@@ -14,8 +15,15 @@ import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextUtils.TruncateAt;
|
||||
import android.text.style.CharacterStyle;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.text.style.StrikethroughSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.text.style.TypefaceSpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ActionMode;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
@@ -35,18 +43,19 @@ import org.thoughtcrime.securesms.components.mention.MentionDeleter;
|
||||
import org.thoughtcrime.securesms.components.mention.MentionRendererDelegate;
|
||||
import org.thoughtcrime.securesms.components.mention.MentionValidatorWatcher;
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType;
|
||||
import org.thoughtcrime.securesms.conversation.MessageStyler;
|
||||
import org.thoughtcrime.securesms.conversation.ui.inlinequery.InlineQuery;
|
||||
import org.thoughtcrime.securesms.conversation.ui.inlinequery.InlineQueryChangedListener;
|
||||
import org.thoughtcrime.securesms.conversation.ui.inlinequery.InlineQueryReplacement;
|
||||
import org.thoughtcrime.securesms.database.model.Mention;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.thoughtcrime.securesms.database.MentionUtil.MENTION_STARTER;
|
||||
@@ -276,6 +285,19 @@ public class ComposeText extends EmojiEditText {
|
||||
return MentionAnnotation.getMentionsFromAnnotations(getText());
|
||||
}
|
||||
|
||||
public boolean hasStyling() {
|
||||
CharSequence trimmed = getTextTrimmed();
|
||||
return FeatureFlags.textFormatting() && (trimmed instanceof Spanned) && MessageStyler.hasStyling((Spanned) trimmed);
|
||||
}
|
||||
|
||||
public @Nullable BodyRangeList getStyling() {
|
||||
if (FeatureFlags.textFormatting()) {
|
||||
return MessageStyler.getStyling(getTextTrimmed());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
if (TextSecurePreferences.isIncognitoKeyboardEnabled(getContext())) {
|
||||
setImeOptions(getImeOptions() | 16777216);
|
||||
@@ -286,6 +308,80 @@ public class ComposeText extends EmojiEditText {
|
||||
addTextChangedListener(new MentionDeleter());
|
||||
mentionValidatorWatcher = new MentionValidatorWatcher();
|
||||
addTextChangedListener(mentionValidatorWatcher);
|
||||
|
||||
if (FeatureFlags.textFormatting()) {
|
||||
setCustomSelectionActionModeCallback(new ActionMode.Callback() {
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
MenuItem copy = menu.findItem(android.R.id.copy);
|
||||
MenuItem cut = menu.findItem(android.R.id.cut);
|
||||
MenuItem paste = menu.findItem(android.R.id.paste);
|
||||
int copyOrder = copy != null ? copy.getOrder() : 0;
|
||||
int cutOrder = cut != null ? cut.getOrder() : 0;
|
||||
int pasteOrder = paste != null ? paste.getOrder() : 0;
|
||||
int largestOrder = Math.max(copyOrder, Math.max(cutOrder, pasteOrder));
|
||||
|
||||
menu.add(0, R.id.edittext_bold, largestOrder, getContext().getString(R.string.TextFormatting_bold));
|
||||
menu.add(0, R.id.edittext_italic, largestOrder, getContext().getString(R.string.TextFormatting_italic));
|
||||
menu.add(0, R.id.edittext_strikethrough, largestOrder, getContext().getString(R.string.TextFormatting_strikethrough));
|
||||
menu.add(0, R.id.edittext_monospace, largestOrder, getContext().getString(R.string.TextFormatting_monospace));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
Editable text = getText();
|
||||
|
||||
if (text == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.getItemId() != R.id.edittext_bold &&
|
||||
item.getItemId() != R.id.edittext_italic &&
|
||||
item.getItemId() != R.id.edittext_strikethrough &&
|
||||
item.getItemId() != R.id.edittext_monospace) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int start = getSelectionStart();
|
||||
int end = getSelectionEnd();
|
||||
|
||||
CharSequence charSequence = text.subSequence(start, end);
|
||||
SpannableString replacement = new SpannableString(charSequence);
|
||||
CharacterStyle style = null;
|
||||
|
||||
if (item.getItemId() == R.id.edittext_bold) {
|
||||
style = MessageStyler.boldStyle();
|
||||
} else if (item.getItemId() == R.id.edittext_italic) {
|
||||
style = MessageStyler.italicStyle();
|
||||
} else if (item.getItemId() == R.id.edittext_strikethrough) {
|
||||
style = MessageStyler.strikethroughStyle();
|
||||
} else if (item.getItemId() == R.id.edittext_monospace) {
|
||||
style = MessageStyler.monoStyle();
|
||||
}
|
||||
|
||||
if (style != null) {
|
||||
replacement.setSpan(style, 0, charSequence.length(), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
|
||||
}
|
||||
|
||||
clearComposingText();
|
||||
|
||||
text.replace(start, end, replacement);
|
||||
|
||||
mode.finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void setHintWithChecks(@Nullable CharSequence newHint) {
|
||||
|
||||
@@ -268,7 +268,14 @@ public class InputPanel extends LinearLayout
|
||||
|
||||
public Optional<QuoteModel> getQuote() {
|
||||
if (quoteView.getQuoteId() > 0 && quoteView.getVisibility() == View.VISIBLE) {
|
||||
return Optional.of(new QuoteModel(quoteView.getQuoteId(), quoteView.getAuthor().getId(), quoteView.getBody().toString(), false, quoteView.getAttachments(), quoteView.getMentions(), quoteView.getQuoteType()));
|
||||
return Optional.of(new QuoteModel(quoteView.getQuoteId(),
|
||||
quoteView.getAuthor().getId(),
|
||||
quoteView.getBody().toString(),
|
||||
false,
|
||||
quoteView.getAttachments(),
|
||||
quoteView.getMentions(),
|
||||
quoteView.getQuoteType(),
|
||||
quoteView.getBodyRanges()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -29,7 +29,9 @@ import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiImageView;
|
||||
import org.thoughtcrime.securesms.components.mention.MentionAnnotation;
|
||||
import org.thoughtcrime.securesms.components.quotes.QuoteViewColorTheme;
|
||||
import org.thoughtcrime.securesms.conversation.MessageStyler;
|
||||
import org.thoughtcrime.securesms.database.model.Mention;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.QuoteModel;
|
||||
@@ -437,7 +439,7 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
|
||||
}
|
||||
|
||||
try {
|
||||
return StoryTextPostModel.parseFrom(body.toString(), id, author.getId());
|
||||
return StoryTextPostModel.parseFrom(body.toString(), id, author.getId(), MessageStyler.getStyling(body));
|
||||
} catch (IOException ioException) {
|
||||
return null;
|
||||
}
|
||||
@@ -471,6 +473,10 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
|
||||
return MentionAnnotation.getMentionsFromAnnotations(body);
|
||||
}
|
||||
|
||||
public @Nullable BodyRangeList getBodyRanges() {
|
||||
return MessageStyler.getStyling(body);
|
||||
}
|
||||
|
||||
private @NonNull ShapeAppearanceModel buildShapeAppearanceForLayoutDirection() {
|
||||
int fourDp = (int) DimensionUnit.DP.toPixels(4);
|
||||
if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) {
|
||||
|
||||
Reference in New Issue
Block a user