@ -156,7 +156,11 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel ;
import org.thoughtcrime.securesms.logging.Log ;
import org.thoughtcrime.securesms.loki.FriendRequestViewDelegate ;
import org.thoughtcrime.securesms.loki.LokiAPIUtilities ;
import org.thoughtcrime.securesms.loki.LokiThreadDatabaseDelegate ;
import org.thoughtcrime.securesms.loki.LokiUserDatabase ;
import org.thoughtcrime.securesms.loki.Mention ;
import org.thoughtcrime.securesms.loki.UserSelectionView ;
import org.thoughtcrime.securesms.mediasend.Media ;
import org.thoughtcrime.securesms.mediasend.MediaSendActivity ;
import org.thoughtcrime.securesms.mms.AttachmentManager ;
@ -224,12 +228,14 @@ import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender ;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage ;
import org.whispersystems.signalservice.api.push.SignalServiceAddress ;
import org.whispersystems.signalservice.loki.api.LokiAPI ;
import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus ;
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus ;
import org.whispersystems.signalservice.loki.utilities.Analytics ;
import java.io.IOException ;
import java.text.SimpleDateFormat ;
import java.util.ArrayList ;
import java.util.Collections ;
import java.util.Date ;
import java.util.LinkedList ;
@ -239,7 +245,9 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean ;
import java.util.concurrent.atomic.AtomicInteger ;
import kotlin.Unit ;
import network.loki.messenger.R ;
import nl.komponents.kovenant.combine.Tuple2 ;
import static org.thoughtcrime.securesms.TransportOption.Type ;
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord ;
@ -309,6 +317,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private Stub < UnverifiedBannerView > unverifiedBannerView ;
private Stub < GroupShareProfileView > groupShareProfileView ;
private TypingStatusTextWatcher typingTextWatcher ;
private MentionTextWatcher mentionTextWatcher ;
private ConversationSearchBottomBar searchNav ;
private MenuItem searchViewItem ;
@ -338,6 +347,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private final DynamicNoActionBarTheme dynamicTheme = new DynamicNoActionBarTheme ( ) ;
private final DynamicLanguage dynamicLanguage = new DynamicLanguage ( ) ;
// Mentions
private UserSelectionView userSelectionView ;
private int currentMentionStartIndex = - 1 ;
private ArrayList < Mention > mentions = new ArrayList < > ( ) ;
private String oldText = "" ;
@Override
protected void onPreCreate ( ) {
@ -389,11 +404,25 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
composeText . addTextChangedListener ( typingTextWatcher ) ;
}
composeText . setSelection ( composeText . length ( ) , composeText . length ( ) ) ;
composeText . addTextChangedListener ( mentionTextWatcher ) ;
userSelectionView . setOnUserSelected ( tuple - > {
Mention mention = new Mention ( currentMentionStartIndex , tuple . getFirst ( ) , tuple . getSecond ( ) ) ;
mentions . add ( mention ) ;
String oldText = composeText . getText ( ) . toString ( ) ;
String newText = oldText . substring ( 0 , currentMentionStartIndex ) + "@" + tuple . getSecond ( ) ;
composeText . setText ( newText ) ;
composeText . setSelection ( newText . length ( ) ) ;
userSelectionView . hide ( ) ;
currentMentionStartIndex = - 1 ;
return Unit . INSTANCE ;
} ) ;
}
} ) ;
}
} ) ;
LokiAPIUtilities . INSTANCE . populateUserIDCacheIfNeeded ( threadId , this ) ;
if ( this . recipient . isGroupRecipient ( ) ) {
if ( this . recipient . getName ( ) . equals ( "Loki Public Chat" ) ) {
Analytics . Companion . getShared ( ) . track ( "Loki Public Chat Opened" ) ;
@ -1552,6 +1581,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 ) ;
ImageButton quickCameraToggle = ViewUtil . findById ( this , R . id . quick_camera_toggle ) ;
ImageButton inlineAttachmentButton = ViewUtil . findById ( this , R . id . inline_attachment_button ) ;
@ -1564,6 +1594,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
attachmentManager = new AttachmentManager ( this , this ) ;
audioRecorder = new AudioRecorder ( this ) ;
typingTextWatcher = new TypingStatusTextWatcher ( ) ;
mentionTextWatcher = new MentionTextWatcher ( ) ;
SendButtonListener sendButtonListener = new SendButtonListener ( ) ;
ComposeKeyPressedListener composeKeyPressedListener = new ComposeKeyPressedListener ( ) ;
@ -2060,12 +2091,20 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private String getMessage ( ) throws InvalidMessageException {
String rawText = composeText . getTextTrimmed ( ) ;
if ( rawText . length ( ) < 1 & & ! attachmentManager . isAttachmentPresent ( ) )
throw new InvalidMessageException ( getString ( R . string . ConversationActivity_message_is_empty_exclamation ) ) ;
return rawText ;
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 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
}
}
return result ;
}
private Pair < String , Optional < Slide > > getSplitMessage ( String rawText , int maxPrimaryMessageSize ) {
@ -2592,6 +2631,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void silentlySetComposeText ( String text ) {
typingTextWatcher . setEnabled ( false ) ;
composeText . setText ( text ) ;
if ( text . isEmpty ( ) ) clearMentions ( ) ;
typingTextWatcher . setEnabled ( true ) ;
}
@ -2706,7 +2746,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private class TypingStatusTextWatcher extends SimpleTextWatcher {
private boolean enabled = true ;
@Override
@ -2721,6 +2760,55 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
}
private class MentionTextWatcher extends SimpleTextWatcher {
@Override
public void onTextChanged ( String text ) {
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 ) ;
}
}
} else if ( text . length ( ) > 0 ) {
if ( currentMentionStartIndex > text . length ( ) ) {
clearMentions ( ) ; // Should never occur
}
int currentEndIndex = text . length ( ) - 1 ;
char lastCharacter = text . charAt ( currentEndIndex ) ;
LokiUserDatabase userDatabase = DatabaseFactory . getLokiUserDatabase ( ConversationActivity . this ) ;
if ( lastCharacter = = '@' ) {
List < Tuple2 < String , String > > users = LokiAPI . Companion . getUsers ( "" , threadId , userDatabase ) ;
currentMentionStartIndex = currentEndIndex ;
userSelectionView . show ( users , threadId ) ;
} else if ( Character . isWhitespace ( lastCharacter ) ) {
currentMentionStartIndex = - 1 ;
userSelectionView . hide ( ) ;
} else {
if ( currentMentionStartIndex ! = - 1 ) {
String query = text . substring ( currentMentionStartIndex + 1 ) ; // + 1 to get rid of the @
List < Tuple2 < String , String > > users = LokiAPI . Companion . getUsers ( query , threadId , userDatabase ) ;
userSelectionView . show ( users , threadId ) ;
}
}
}
}
}
private void clearMentions ( ) {
oldText = "" ;
currentMentionStartIndex = - 1 ;
mentions . clear ( ) ;
}
@Override
public void setThreadId ( long threadId ) {
this . threadId = threadId ;