From e423ec88481143aa5ce6a293001bb4c9375af60d Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 11 Oct 2019 16:37:28 +1100 Subject: [PATCH] Change mentions approach --- ...cell_mention_candidate_selection_view.xml} | 4 +- res/layout/view_user_selection.xml | 2 +- .../securesms/ConversationListItem.java | 2 +- .../conversation/ConversationActivity.java | 67 +++++++++---------- .../securesms/jobs/PushDecryptJob.java | 2 +- .../securesms/loki/LokiAPIUtilities.kt | 6 +- .../thoughtcrime/securesms/loki/Mention.kt | 3 - ...ew.kt => MentionCandidateSelectionView.kt} | 38 +++++------ ...t => MentionCandidateSelectionViewCell.kt} | 18 ++--- .../securesms/loki/MentionUtilities.kt | 6 +- 10 files changed, 71 insertions(+), 77 deletions(-) rename res/layout/{cell_user_selection_view.xml => cell_mention_candidate_selection_view.xml} (92%) delete mode 100644 src/org/thoughtcrime/securesms/loki/Mention.kt rename src/org/thoughtcrime/securesms/loki/{UserSelectionView.kt => MentionCandidateSelectionView.kt} (52%) rename src/org/thoughtcrime/securesms/loki/{UserSelectionViewCell.kt => MentionCandidateSelectionViewCell.kt} (60%) 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)