diff --git a/res/layout/cell_user_selection_view.xml b/res/layout/cell_mention_candidate_selection_view.xml
similarity index 92%
rename from res/layout/cell_user_selection_view.xml
rename to res/layout/cell_mention_candidate_selection_view.xml
index 0219e6d069..3ea8ade842 100644
--- a/res/layout/cell_user_selection_view.xml
+++ b/res/layout/cell_mention_candidate_selection_view.xml
@@ -1,5 +1,5 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/res/layout/view_user_selection.xml b/res/layout/view_user_selection.xml
index ee014962aa..152e0d95f9 100644
--- a/res/layout/view_user_selection.xml
+++ b/res/layout/view_user_selection.xml
@@ -1,5 +1,5 @@
- mentions = new ArrayList<>();
private String oldText = "";
@@ -405,15 +404,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
composeText.setSelection(composeText.length(), composeText.length());
composeText.addTextChangedListener(mentionTextWatcher);
- userSelectionView.setOnUserSelected(tuple -> {
- Mention mention = new Mention(currentMentionStartIndex, tuple.getFirst(), tuple.getSecond());
- mentions.add(mention);
+ mentionCandidateSelectionView.setOnMentionCandidateSelected( mentionCandidate -> {
+ mentions.add(mentionCandidate);
String oldText = composeText.getText().toString();
- String newText = oldText.substring(0, currentMentionStartIndex) + "@" + tuple.getSecond();
+ String newText = oldText.substring(0, currentMentionStartIndex) + "@" + mentionCandidate.getDisplayName();
composeText.setText(newText);
composeText.setSelection(newText.length());
- userSelectionView.hide();
currentMentionStartIndex = -1;
+ mentionCandidateSelectionView.hide();
+ ConversationActivity.this.oldText = newText;
return Unit.INSTANCE;
});
}
@@ -421,7 +420,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
});
- LokiAPIUtilities.INSTANCE.populateUserIDCacheIfNeeded(threadId, this);
+ LokiAPIUtilities.INSTANCE.populateUserHexEncodedPublicKeyCacheIfNeeded(threadId, this);
if (this.recipient.isGroupRecipient()) {
if (this.recipient.getName().equals("Loki Public Chat")) {
@@ -1581,7 +1580,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
inlineAttachmentToggle = ViewUtil.findById(this, R.id.inline_attachment_container);
inputPanel = ViewUtil.findById(this, R.id.bottom_panel);
searchNav = ViewUtil.findById(this, R.id.conversation_search_nav);
- userSelectionView = ViewUtil.findById(this, R.id.userSelectionView);
+ mentionCandidateSelectionView = ViewUtil.findById(this, R.id.userSelectionView);
ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle);
ImageButton inlineAttachmentButton = ViewUtil.findById(this, R.id.inline_attachment_button);
@@ -2093,15 +2092,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private String getMessage() throws InvalidMessageException {
String result = composeText.getTextTrimmed();
if (result.length() < 1 && !attachmentManager.isAttachmentPresent()) throw new InvalidMessageException();
- int shift = 0;
for (Mention mention : mentions) {
try {
- int startIndex = mention.getLocationInString() + shift;
+ int startIndex = result.indexOf("@" + mention.getDisplayName());
int endIndex = startIndex + mention.getDisplayName().length() + 1; // + 1 to include the @
- shift = shift + mention.getHexEncodedPublicKey().length() - mention.getDisplayName().length();
result = result.substring(0, startIndex) + "@" + mention.getHexEncodedPublicKey() + result.substring(endIndex);
} catch (Exception exception) {
- // Do nothing
+ Log.d("Loki", "Couldn't process mention due to error: " + exception.toString() + ".");
}
}
return result;
@@ -2631,7 +2628,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void silentlySetComposeText(String text) {
typingTextWatcher.setEnabled(false);
composeText.setText(text);
- if (text.isEmpty()) clearMentions();
+ if (text.isEmpty()) resetMentions();
typingTextWatcher.setEnabled(true);
}
@@ -2767,43 +2764,43 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
boolean isBackspace = text.length() < oldText.length();
if (isBackspace) {
currentMentionStartIndex = -1;
- for (Mention mention : mentions) {
- boolean isValid;
- if (mention.getLocationInString() > (text.length() - 1)) {
- isValid = false;
- } else {
- isValid = text.substring(mention.getLocationInString()).startsWith("@" + mention.getDisplayName());
- }
- if (!isValid) {
- mentions.remove(mention);
+ mentionCandidateSelectionView.hide();
+ try {
+ for (Mention mention : mentions) {
+ if (!text.contains(mention.getDisplayName())) {
+ mentions.remove(mention);
+ }
}
+ } catch (Exception exception) {
+ mentions.clear(); // TODO: Dirty workaround for ConcurrentModificationException
}
} else if (text.length() > 0) {
if (currentMentionStartIndex > text.length()) {
- clearMentions(); // Should never occur
+ resetMentions(); // Should never occur
}
- int currentEndIndex = text.length() - 1;
- char lastCharacter = text.charAt(currentEndIndex);
+ int lastCharacterIndex = text.length() - 1;
+ char lastCharacter = text.charAt(lastCharacterIndex);
LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(ConversationActivity.this);
if (lastCharacter == '@') {
- List> users = LokiAPI.Companion.getUsers("", threadId, userDatabase);
- currentMentionStartIndex = currentEndIndex;
- userSelectionView.show(users, threadId);
+ List mentionCandidates = LokiAPI.Companion.getMentionCandidates("", threadId, userDatabase);
+ currentMentionStartIndex = lastCharacterIndex;
+ mentionCandidateSelectionView.show(mentionCandidates, threadId);
} else if (Character.isWhitespace(lastCharacter)) {
currentMentionStartIndex = -1;
- userSelectionView.hide();
+ mentionCandidateSelectionView.hide();
} else {
if (currentMentionStartIndex != -1) {
String query = text.substring(currentMentionStartIndex + 1); // + 1 to get rid of the @
- List> users = LokiAPI.Companion.getUsers(query, threadId, userDatabase);
- userSelectionView.show(users, threadId);
+ List mentionCandidates = LokiAPI.Companion.getMentionCandidates(query, threadId, userDatabase);
+ mentionCandidateSelectionView.show(mentionCandidates, threadId);
}
}
}
+ ConversationActivity.this.oldText = text;
}
}
- private void clearMentions() {
+ private void resetMentions() {
oldText = "";
currentMentionStartIndex = -1;
mentions.clear();
diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
index 59a7ec1e27..0007d8c87c 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
@@ -1025,7 +1025,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
// Loki - Cache the user hex encoded public key (for mentions)
if (threadId != null) {
- LokiAPIUtilities.INSTANCE.populateUserIDCacheIfNeeded(threadId, context);
+ LokiAPIUtilities.INSTANCE.populateUserHexEncodedPublicKeyCacheIfNeeded(threadId, context);
LokiAPI.Companion.cache(textMessage.getSender().serialize(), threadId);
}
diff --git a/src/org/thoughtcrime/securesms/loki/LokiAPIUtilities.kt b/src/org/thoughtcrime/securesms/loki/LokiAPIUtilities.kt
index 3c110606c5..aea9fd4573 100644
--- a/src/org/thoughtcrime/securesms/loki/LokiAPIUtilities.kt
+++ b/src/org/thoughtcrime/securesms/loki/LokiAPIUtilities.kt
@@ -8,8 +8,8 @@ import org.whispersystems.signalservice.loki.api.LokiAPI
object LokiAPIUtilities {
- fun populateUserIDCacheIfNeeded(threadID: Long, context: Context) {
- if (LokiAPI.userIDCache[threadID] != null) { return }
+ fun populateUserHexEncodedPublicKeyCacheIfNeeded(threadID: Long, context: Context) {
+ if (LokiAPI.userHexEncodedPublicKeyCache[threadID] != null) { return }
val result = mutableSetOf()
val messageDatabase = DatabaseFactory.getMmsSmsDatabase(context)
val reader = messageDatabase.readerFor(messageDatabase.getConversation(threadID))
@@ -24,6 +24,6 @@ object LokiAPIUtilities {
}
reader.close()
result.add(TextSecurePreferences.getLocalNumber(context))
- LokiAPI.userIDCache[threadID] = result
+ LokiAPI.userHexEncodedPublicKeyCache[threadID] = result
}
}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/Mention.kt b/src/org/thoughtcrime/securesms/loki/Mention.kt
deleted file mode 100644
index e15cf44ef8..0000000000
--- a/src/org/thoughtcrime/securesms/loki/Mention.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package org.thoughtcrime.securesms.loki
-
-data class Mention(val locationInString: Int, val hexEncodedPublicKey: String, val displayName: String)
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/UserSelectionView.kt b/src/org/thoughtcrime/securesms/loki/MentionCandidateSelectionView.kt
similarity index 52%
rename from src/org/thoughtcrime/securesms/loki/UserSelectionView.kt
rename to src/org/thoughtcrime/securesms/loki/MentionCandidateSelectionView.kt
index ab5da3cf7e..9465d2cfa5 100644
--- a/src/org/thoughtcrime/securesms/loki/UserSelectionView.kt
+++ b/src/org/thoughtcrime/securesms/loki/MentionCandidateSelectionView.kt
@@ -7,38 +7,38 @@ import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.ListView
-import nl.komponents.kovenant.combine.Tuple2
import org.thoughtcrime.securesms.database.DatabaseFactory
+import org.whispersystems.signalservice.loki.messaging.Mention
-class UserSelectionView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : ListView(context, attrs, defStyleAttr) {
- private var users = listOf>()
- set(newValue) { field = newValue; userSelectionViewAdapter.users = newValue }
+class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : ListView(context, attrs, defStyleAttr) {
+ private var mentionCandidates = listOf()
+ set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.mentionCandidates = newValue }
private var hasGroupContext = false
- var onUserSelected: ((Tuple2) -> Unit)? = null
+ var onMentionCandidateSelected: ((Mention) -> Unit)? = null
- private val userSelectionViewAdapter by lazy { Adapter(context) }
+ private val mentionCandidateSelectionViewAdapter by lazy { Adapter(context) }
private class Adapter(private val context: Context) : BaseAdapter() {
- var users = listOf>()
+ var mentionCandidates = listOf()
set(newValue) { field = newValue; notifyDataSetChanged() }
var hasGroupContext = false
override fun getCount(): Int {
- return users.count()
+ return mentionCandidates.count()
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
- override fun getItem(position: Int): Tuple2 {
- return users[position]
+ override fun getItem(position: Int): Mention {
+ return mentionCandidates[position]
}
override fun getView(position: Int, cellToBeReused: View?, parent: ViewGroup): View {
- val cell = cellToBeReused as UserSelectionViewCell? ?: UserSelectionViewCell.inflate(LayoutInflater.from(context), parent)
- val user = getItem(position)
- cell.user = user
+ val cell = cellToBeReused as MentionCandidateSelectionViewCell? ?: MentionCandidateSelectionViewCell.inflate(LayoutInflater.from(context), parent)
+ val mentionCandidate = getItem(position)
+ cell.mentionCandidate = mentionCandidate
cell.hasGroupContext = hasGroupContext
return cell
}
@@ -48,18 +48,18 @@ class UserSelectionView(context: Context, attrs: AttributeSet?, defStyleAttr: In
constructor(context: Context) : this(context, null)
init {
- adapter = userSelectionViewAdapter
- userSelectionViewAdapter.users = users
+ adapter = mentionCandidateSelectionViewAdapter
+ mentionCandidateSelectionViewAdapter.mentionCandidates = mentionCandidates
setOnItemClickListener { _, _, position, _ ->
- onUserSelected?.invoke(users[position])
+ onMentionCandidateSelected?.invoke(mentionCandidates[position])
}
}
- fun show(users: List>, threadID: Long) {
+ fun show(mentionCandidates: List, threadID: Long) {
hasGroupContext = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadID)!!.isGroupRecipient
- this.users = users
+ this.mentionCandidates = mentionCandidates
val layoutParams = this.layoutParams as ViewGroup.LayoutParams
- layoutParams.height = toPx(6 + Math.min(users.count(), 4) * 52, resources)
+ layoutParams.height = toPx(6 + Math.min(mentionCandidates.count(), 4) * 52, resources)
this.layoutParams = layoutParams
}
diff --git a/src/org/thoughtcrime/securesms/loki/UserSelectionViewCell.kt b/src/org/thoughtcrime/securesms/loki/MentionCandidateSelectionViewCell.kt
similarity index 60%
rename from src/org/thoughtcrime/securesms/loki/UserSelectionViewCell.kt
rename to src/org/thoughtcrime/securesms/loki/MentionCandidateSelectionViewCell.kt
index 739e55d098..8656948d59 100644
--- a/src/org/thoughtcrime/securesms/loki/UserSelectionViewCell.kt
+++ b/src/org/thoughtcrime/securesms/loki/MentionCandidateSelectionViewCell.kt
@@ -8,13 +8,13 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewOutlineProvider
import android.widget.LinearLayout
-import kotlinx.android.synthetic.main.cell_user_selection_view.view.*
+import kotlinx.android.synthetic.main.cell_mention_candidate_selection_view.view.*
import network.loki.messenger.R
-import nl.komponents.kovenant.combine.Tuple2
import org.whispersystems.signalservice.loki.api.LokiGroupChatAPI
+import org.whispersystems.signalservice.loki.messaging.Mention
-class UserSelectionViewCell(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) {
- var user = Tuple2("", "")
+class MentionCandidateSelectionViewCell(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) {
+ var mentionCandidate = Mention("", "")
set(newValue) { field = newValue; update() }
var hasGroupContext = false
@@ -23,8 +23,8 @@ class UserSelectionViewCell(context: Context, attrs: AttributeSet?, defStyleAttr
companion object {
- fun inflate(layoutInflater: LayoutInflater, parent: ViewGroup): UserSelectionViewCell {
- return layoutInflater.inflate(R.layout.cell_user_selection_view, parent, false) as UserSelectionViewCell
+ fun inflate(layoutInflater: LayoutInflater, parent: ViewGroup): MentionCandidateSelectionViewCell {
+ return layoutInflater.inflate(R.layout.cell_mention_candidate_selection_view, parent, false) as MentionCandidateSelectionViewCell
}
}
@@ -40,9 +40,9 @@ class UserSelectionViewCell(context: Context, attrs: AttributeSet?, defStyleAttr
}
private fun update() {
- displayNameTextView.text = user.second
- profilePictureImageView.update(user.first)
- val isUserModerator = LokiGroupChatAPI.isUserModerator(user.first, LokiGroupChatAPI.publicChatServerID, LokiGroupChatAPI.publicChatServer)
+ displayNameTextView.text = mentionCandidate.displayName
+ profilePictureImageView.update(mentionCandidate.hexEncodedPublicKey)
+ val isUserModerator = LokiGroupChatAPI.isUserModerator(mentionCandidate.hexEncodedPublicKey, LokiGroupChatAPI.publicChatServerID, LokiGroupChatAPI.publicChatServer)
moderatorIconImageView.visibility = if (isUserModerator && hasGroupContext) View.VISIBLE else View.GONE
}
}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/MentionUtilities.kt b/src/org/thoughtcrime/securesms/loki/MentionUtilities.kt
index d9f0643cad..187694d3ff 100644
--- a/src/org/thoughtcrime/securesms/loki/MentionUtilities.kt
+++ b/src/org/thoughtcrime/securesms/loki/MentionUtilities.kt
@@ -27,12 +27,12 @@ object MentionUtilities {
var startIndex = 0
if (matcher.find(startIndex) && isGroupThread) {
while (true) {
- val userID = text.subSequence(matcher.start() + 1, matcher.end()).toString() // +1 to get rid of the @
- val userDisplayName: String? = if (userID.toLowerCase() == TextSecurePreferences.getLocalNumber(context).toLowerCase()) {
+ val hexEncodedPublicKey = text.subSequence(matcher.start() + 1, matcher.end()).toString() // +1 to get rid of the @
+ val userDisplayName: String? = if (hexEncodedPublicKey.toLowerCase() == TextSecurePreferences.getLocalNumber(context).toLowerCase()) {
TextSecurePreferences.getProfileName(context)
} else {
val publicChatID = LokiGroupChatAPI.publicChatServer + "." + LokiGroupChatAPI.publicChatServerID
- DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChatID, userID)
+ DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChatID, hexEncodedPublicKey)
}
if (userDisplayName != null) {
text = text.subSequence(0, matcher.start()).toString() + "@" + userDisplayName + text.subSequence(matcher.end(), text.length)