Implement mention rendering

This commit is contained in:
Niels Andriesse 2019-10-09 16:41:14 +11:00
parent 7116f2502a
commit 5901967eee
3 changed files with 54 additions and 21 deletions

View File

@ -2,6 +2,7 @@
<resources>
<!-- Loki -->
<color name="loki_green">#78be20</color>
<color name="loki_dark_green">#419B41</color>
<color name="loki_darkest_gray">#0a0a0a</color>
<!-- Loki -->

View File

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

View File

@ -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,23 +191,21 @@ 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()
}
}
} else {
startActivity(Intent(this, DisplayNameActivity::class.java))
finish()