From 5901967eee8158146c00aee43bcf61c174b702f0 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Wed, 9 Oct 2019 16:41:14 +1100 Subject: [PATCH] Implement mention rendering --- res/values/colors.xml | 1 + .../conversation/ConversationItem.java | 50 ++++++++++++++++--- .../securesms/loki/SeedActivity.kt | 24 ++++----- 3 files changed, 54 insertions(+), 21 deletions(-) diff --git a/res/values/colors.xml b/res/values/colors.xml index e9e32991cc..593a27dbd2 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -2,6 +2,7 @@ #78be20 + #419B41 #0a0a0a diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java index 798d843ab0..236966da66 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -42,6 +42,7 @@ import android.text.style.ForegroundColorSpan; import android.text.style.URLSpan; import android.text.util.Linkify; import android.util.AttributeSet; +import android.util.Range; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; @@ -114,11 +115,14 @@ import org.thoughtcrime.securesms.util.views.Stub; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.loki.api.LokiGroupChatAPI; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import network.loki.messenger.R; @@ -258,7 +262,7 @@ public class ConversationItem extends LinearLayout setMessageShape(messageRecord, previousMessageRecord, nextMessageRecord, groupThread); setMediaAttributes(messageRecord, previousMessageRecord, nextMessageRecord, conversationRecipient, groupThread); setInteractionState(messageRecord, pulseHighlight); - setBodyText(messageRecord, searchQuery); + setBodyText(messageRecord, searchQuery, groupThread); setBubbleState(messageRecord); setStatusIcons(messageRecord); setContactPhoto(recipient); @@ -464,7 +468,7 @@ public class ConversationItem extends LinearLayout !StickerUrl.isValidShareLink(linkPreview.getUrl()); } - private void setBodyText(MessageRecord messageRecord, @Nullable String searchQuery) { + private void setBodyText(MessageRecord messageRecord, @Nullable String searchQuery, boolean isGroupThread) { bodyText.setClickable(false); bodyText.setFocusable(false); bodyText.setTextSize(TypedValue.COMPLEX_UNIT_SP, TextSecurePreferences.getMessageBodyTextSize(context)); @@ -472,9 +476,9 @@ public class ConversationItem extends LinearLayout if (isCaptionlessMms(messageRecord)) { bodyText.setVisibility(View.GONE); } else { - Spannable styledText = linkifyMessageBody(messageRecord.getDisplayBody(getContext()), batchSelected.isEmpty()); - styledText = SearchUtil.getHighlightedSpan(locale, () -> new BackgroundColorSpan(Color.YELLOW), styledText, searchQuery); - styledText = SearchUtil.getHighlightedSpan(locale, () -> new ForegroundColorSpan(Color.BLACK), styledText, searchQuery); + Spannable text = linkifyMessageBody(highlightMentions(messageRecord.getDisplayBody(context), isGroupThread), batchSelected.isEmpty()); + text = SearchUtil.getHighlightedSpan(locale, () -> new BackgroundColorSpan(Color.YELLOW), text, searchQuery); + text = SearchUtil.getHighlightedSpan(locale, () -> new ForegroundColorSpan(Color.BLACK), text, searchQuery); if (hasExtraText(messageRecord)) { bodyText.setOverflowText(getLongMessageSpan(messageRecord)); @@ -482,7 +486,7 @@ public class ConversationItem extends LinearLayout bodyText.setOverflowText(null); } - bodyText.setText(styledText); + bodyText.setText(text); bodyText.setVisibility(View.VISIBLE); } } @@ -772,6 +776,40 @@ public class ConversationItem extends LinearLayout return messageBody; } + private SpannableString highlightMentions(CharSequence text, boolean isGroupThread) { + Pattern pattern = Pattern.compile("@\\w*"); + Matcher matcher = pattern.matcher(text); + ArrayList> mentions = new ArrayList<>(); + if (matcher.find() && isGroupThread) { + while (true) { + CharSequence userID = text.subSequence(matcher.start() + 1, matcher.end()); // +1 to get rid of the @ + Integer matchEnd; + String userDisplayName; + if (userID.equals(TextSecurePreferences.getLocalNumber(context))) { + userDisplayName = TextSecurePreferences.getProfileName(context); + } else { + String publicChatID = LokiGroupChatAPI.getPublicChatServer() + "." + LokiGroupChatAPI.getPublicChatServerID(); + userDisplayName = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChatID, userID.toString()); + } + if (userDisplayName != null) { + text = text.subSequence(0, matcher.start()) + "@" + userDisplayName + text.subSequence(matcher.end(), Math.max(text.length(), matcher.end())); + matchEnd = matcher.start() + 1 + userDisplayName.length(); + mentions.add(Range.create(matcher.start(), matchEnd)); + } else { + matchEnd = matcher.end(); + } + matcher = pattern.matcher(text.subSequence(matchEnd, Math.max(text.length(), matchEnd))); + if (!matcher.find()) { break; } + } + } + SpannableString result = new SpannableString(text); + for (Range range : mentions) { + int highlightColor = (messageRecord.isOutgoing()) ? getResources().getColor(R.color.loki_dark_green) : getResources().getColor(R.color.loki_green); + result.setSpan(new BackgroundColorSpan(highlightColor), range.getLower(), range.getUpper(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + return result; + } + private void setStatusIcons(MessageRecord messageRecord) { bodyText.setCompoundDrawablesWithIntrinsicBounds(0, 0, messageRecord.isKeyExchange() ? R.drawable.ic_menu_login : 0, 0); diff --git a/src/org/thoughtcrime/securesms/loki/SeedActivity.kt b/src/org/thoughtcrime/securesms/loki/SeedActivity.kt index 8da00fe60c..a98b6c5a46 100644 --- a/src/org/thoughtcrime/securesms/loki/SeedActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/SeedActivity.kt @@ -9,9 +9,6 @@ import android.view.View import android.view.inputmethod.InputMethodManager import android.widget.Toast import kotlinx.android.synthetic.main.activity_seed.* -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import network.loki.messenger.R import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.BaseActionBarActivity @@ -180,13 +177,12 @@ class SeedActivity : BaseActionBarActivity(), DeviceLinkingDialogDelegate { IdentityKeyUtil.generateIdentityKeyPair(this, seed) } val keyPair = IdentityKeyUtil.getIdentityKeyPair(this) - val publicKey = keyPair.publicKey - val hexEncodedPublicKey = keyPair.hexEncodedPublicKey + val userHexEncodedPublicKey = keyPair.hexEncodedPublicKey val registrationID = KeyHelper.generateRegistrationId(false) TextSecurePreferences.setLocalRegistrationId(this, registrationID) - DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(hexEncodedPublicKey), publicKey, + DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(userHexEncodedPublicKey), keyPair.publicKey, IdentityDatabase.VerifiedStatus.VERIFIED, true, System.currentTimeMillis(), true) - TextSecurePreferences.setLocalNumber(this, hexEncodedPublicKey) + TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey) when (mode) { Mode.Register -> Analytics.shared.track("Seed Created") Mode.Restore -> Analytics.shared.track("Seed Restored") @@ -195,22 +191,20 @@ class SeedActivity : BaseActionBarActivity(), DeviceLinkingDialogDelegate { if (mode == Mode.Link) { TextSecurePreferences.setHasSeenWelcomeScreen(this, true) TextSecurePreferences.setPromptedPushRegistration(this, true) - val primaryDevicePublicKey = publicKeyEditText.text.trim().toString() - val authorisation = PairingAuthorisation(primaryDevicePublicKey, hexEncodedPublicKey).sign(PairingAuthorisation.Type.REQUEST, keyPair.privateKey.serialize()) + val masterHexEncodedPublicKey = publicKeyEditText.text.trim().toString() + val authorisation = PairingAuthorisation(masterHexEncodedPublicKey, userHexEncodedPublicKey).sign(PairingAuthorisation.Type.REQUEST, keyPair.privateKey.serialize()) if (authorisation == null) { - Log.d("Loki", "Failed to sign outgoing pairing request.") + Log.d("Loki", "Failed to sign pairing request.") resetForRegistration() - return Toast.makeText(application, "Failed to link device.", Toast.LENGTH_SHORT).show() + return Toast.makeText(application, "Couldn't start device linking process.", Toast.LENGTH_SHORT).show() } val application = ApplicationContext.getInstance(this) application.startLongPollingIfNeeded() application.setUpP2PAPI() application.setUpStorageAPIIfNeeded() DeviceLinkingDialog.show(this, DeviceLinkingView.Mode.Slave, this) - CoroutineScope(Dispatchers.Main).launch { - retryIfNeeded(8) { - sendPairingAuthorisationMessage(this@SeedActivity, authorisation.primaryDevicePublicKey, authorisation).get() - } + retryIfNeeded(8) { + sendPairingAuthorisationMessage(this@SeedActivity, authorisation.primaryDevicePublicKey, authorisation).get() } } else { startActivity(Intent(this, DisplayNameActivity::class.java))