mirror of
https://github.com/oxen-io/session-android.git
synced 2025-04-21 21:51:31 +00:00
Implement the new protocol
This commit is contained in:
parent
455471b20e
commit
06f547dc88
@ -61,14 +61,13 @@ import org.thoughtcrime.securesms.logging.PersistentLogger;
|
|||||||
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
|
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
|
||||||
import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
||||||
import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker;
|
import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker;
|
||||||
import org.thoughtcrime.securesms.loki.api.PublicChatManager;
|
|
||||||
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager;
|
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager;
|
||||||
|
import org.thoughtcrime.securesms.loki.api.PublicChatManager;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.EphemeralMessage;
|
import org.thoughtcrime.securesms.loki.protocol.PushSessionRequestMessageSendJob;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
|
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.Broadcaster;
|
import org.thoughtcrime.securesms.loki.utilities.Broadcaster;
|
||||||
import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier;
|
import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier;
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
@ -601,8 +600,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
if (hasSentOrProcessedSessionRequest) { return; }
|
if (hasSentOrProcessedSessionRequest) { return; }
|
||||||
// Send the session request
|
// Send the session request
|
||||||
DatabaseFactory.getLokiAPIDatabase(this).setSessionRequestTimestamp(publicKey, new Date().getTime());
|
DatabaseFactory.getLokiAPIDatabase(this).setSessionRequestTimestamp(publicKey, new Date().getTime());
|
||||||
EphemeralMessage sessionRequest = EphemeralMessage.createSessionRequest(publicKey);
|
PushSessionRequestMessageSendJob job = new PushSessionRequestMessageSendJob(publicKey);
|
||||||
jobManager.add(new PushEphemeralMessageSendJob(sessionRequest));
|
jobManager.add(job);
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
@ -2359,6 +2359,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
final long id = fragment.stageOutgoingMessage(outgoingMessage);
|
final long id = fragment.stageOutgoingMessage(outgoingMessage);
|
||||||
|
|
||||||
|
if (!recipient.isGroupRecipient()) {
|
||||||
|
ApplicationContext.getInstance(this).sendSessionRequestIfNeeded(recipient.getAddress().serialize());
|
||||||
|
}
|
||||||
|
|
||||||
new AsyncTask<Void, Void, Long>() {
|
new AsyncTask<Void, Void, Long>() {
|
||||||
@Override
|
@Override
|
||||||
protected Long doInBackground(Void... param) {
|
protected Long doInBackground(Void... param) {
|
||||||
@ -2402,6 +2406,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
silentlySetComposeText("");
|
silentlySetComposeText("");
|
||||||
final long id = fragment.stageOutgoingMessage(message);
|
final long id = fragment.stageOutgoingMessage(message);
|
||||||
|
|
||||||
|
if (!recipient.isGroupRecipient()) {
|
||||||
|
ApplicationContext.getInstance(this).sendSessionRequestIfNeeded(recipient.getAddress().serialize());
|
||||||
|
}
|
||||||
|
|
||||||
new AsyncTask<OutgoingTextMessage, Void, Long>() {
|
new AsyncTask<OutgoingTextMessage, Void, Long>() {
|
||||||
@Override
|
@Override
|
||||||
protected Long doInBackground(OutgoingTextMessage... messages) {
|
protected Long doInBackground(OutgoingTextMessage... messages) {
|
||||||
|
@ -14,8 +14,8 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint
|
|||||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
|
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceOpenGroupUpdateJob;
|
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceOpenGroupUpdateJob;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
|
|
||||||
import org.thoughtcrime.securesms.loki.protocol.PushNullMessageSendJob;
|
import org.thoughtcrime.securesms.loki.protocol.PushNullMessageSendJob;
|
||||||
|
import org.thoughtcrime.securesms.loki.protocol.PushSessionRequestMessageSendJob;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -72,7 +72,7 @@ public final class JobManagerFactories {
|
|||||||
put(TrimThreadJob.KEY, new TrimThreadJob.Factory());
|
put(TrimThreadJob.KEY, new TrimThreadJob.Factory());
|
||||||
put(TypingSendJob.KEY, new TypingSendJob.Factory());
|
put(TypingSendJob.KEY, new TypingSendJob.Factory());
|
||||||
put(UpdateApkJob.KEY, new UpdateApkJob.Factory());
|
put(UpdateApkJob.KEY, new UpdateApkJob.Factory());
|
||||||
put(PushEphemeralMessageSendJob.KEY, new PushEphemeralMessageSendJob.Factory());
|
put(PushSessionRequestMessageSendJob.KEY, new PushSessionRequestMessageSendJob.Factory());
|
||||||
put(MultiDeviceOpenGroupUpdateJob.KEY, new MultiDeviceOpenGroupUpdateJob.Factory());
|
put(MultiDeviceOpenGroupUpdateJob.KEY, new MultiDeviceOpenGroupUpdateJob.Factory());
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,7 @@ import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
|||||||
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
|
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol;
|
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol;
|
||||||
|
import org.thoughtcrime.securesms.loki.protocol.PushNullMessageSendJob;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
|
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
|
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
|
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
|
||||||
@ -266,13 +267,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loki - Handle pre key bundle message if needed
|
|
||||||
SessionManagementProtocol.handlePreKeyBundleMessageIfNeeded(context, content);
|
SessionManagementProtocol.handlePreKeyBundleMessageIfNeeded(context, content);
|
||||||
|
|
||||||
// Loki - Handle session request if needed
|
|
||||||
if (SessionManagementProtocol.handleSessionRequestIfNeeded(context, content)) { return; } // Don't process the message any further
|
|
||||||
|
|
||||||
// Loki - Handle profile update if needed
|
|
||||||
SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content);
|
SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content);
|
||||||
|
|
||||||
if (content.getDeviceLink().isPresent()) {
|
if (content.getDeviceLink().isPresent()) {
|
||||||
@ -281,7 +277,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
SignalServiceDataMessage message = content.getDataMessage().get();
|
SignalServiceDataMessage message = content.getDataMessage().get();
|
||||||
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
|
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
|
||||||
|
|
||||||
// Loki - Handle unlinking request if needed
|
|
||||||
if (message.isDeviceUnlinkingRequest()) {
|
if (message.isDeviceUnlinkingRequest()) {
|
||||||
MultiDeviceProtocol.handleUnlinkingRequestIfNeeded(context, content);
|
MultiDeviceProtocol.handleUnlinkingRequestIfNeeded(context, content);
|
||||||
} else {
|
} else {
|
||||||
@ -341,14 +336,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
} else if (content.getTypingMessage().isPresent()) {
|
} else if (content.getTypingMessage().isPresent()) {
|
||||||
handleTypingMessage(content, content.getTypingMessage().get());
|
handleTypingMessage(content, content.getTypingMessage().get());
|
||||||
} else if (content.getNullMessage().isPresent()) {
|
} else if (content.getNullMessage().isPresent()) {
|
||||||
// Loki - This is needed for compatibility with refactored desktop clients
|
if (content.preKeyBundleMessage.isPresent()) {
|
||||||
// ========
|
ApplicationContext.getInstance(context).getJobManager().add(new PushNullMessageSendJob(content.getSender()));
|
||||||
// if (content.isFriendRequest()) {
|
} else {
|
||||||
// ApplicationContext.getInstance(context).getJobManager().add(new PushNullMessageSendJob(content.getSender()));
|
Log.w(TAG, "Got unrecognized message...");
|
||||||
// } else {
|
}
|
||||||
// Log.w(TAG, "Got unrecognized message...");
|
|
||||||
// }
|
|
||||||
// ========
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resetRecipientToPush(Recipient.from(context, Address.fromSerialized(content.getSender()), false));
|
resetRecipientToPush(Recipient.from(context, Address.fromSerialized(content.getSender()), false));
|
||||||
@ -645,11 +637,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
|
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loki - Handle profile key update if needed
|
|
||||||
handleProfileKey(content, message.getMessage());
|
handleProfileKey(content, message.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loki - Update profile if needed
|
|
||||||
SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content);
|
SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content);
|
||||||
|
|
||||||
if (threadId != null) {
|
if (threadId != null) {
|
||||||
@ -754,7 +744,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||||
database.beginTransaction();
|
database.beginTransaction();
|
||||||
|
|
||||||
// Loki - Ignore message if it has no body and no attachments
|
// Ignore message if it has no body and no attachments
|
||||||
if (mediaMessage.getBody().isEmpty() && mediaMessage.getAttachments().isEmpty() && mediaMessage.getLinkPreviews().isEmpty()) {
|
if (mediaMessage.getBody().isEmpty() && mediaMessage.getAttachments().isEmpty() && mediaMessage.getLinkPreviews().isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
|||||||
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
||||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
import org.whispersystems.libsignal.state.PreKeyBundle;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||||
@ -196,11 +197,17 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
|||||||
|
|
||||||
log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent());
|
log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent());
|
||||||
|
|
||||||
|
PreKeyBundle preKeyBundle = null;
|
||||||
|
if (message.isEndSession()) {
|
||||||
|
preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(destination.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
|
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
|
||||||
.withTimestamp(message.getDateSent())
|
.withTimestamp(message.getDateSent())
|
||||||
.withBody(message.getBody())
|
.withBody(message.getBody())
|
||||||
.withExpiration((int)(message.getExpiresIn() / 1000))
|
.withExpiration((int)(message.getExpiresIn() / 1000))
|
||||||
.withProfileKey(profileKey.orNull())
|
.withProfileKey(profileKey.orNull())
|
||||||
|
.withPreKeyBundle(preKeyBundle)
|
||||||
.asEndSessionMessage(message.isEndSession())
|
.asEndSessionMessage(message.isEndSession())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -47,11 +47,11 @@ object LokiPushNotificationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun register(token: String, hexEncodedPublicKey: String, context: Context?, force: Boolean) {
|
fun register(token: String, publicKey: String, context: Context?, force: Boolean) {
|
||||||
val oldToken = TextSecurePreferences.getFCMToken(context)
|
val oldToken = TextSecurePreferences.getFCMToken(context)
|
||||||
val lastUploadDate = TextSecurePreferences.getLastFCMUploadTime(context)
|
val lastUploadDate = TextSecurePreferences.getLastFCMUploadTime(context)
|
||||||
if (!force && token == oldToken && System.currentTimeMillis() - lastUploadDate < tokenExpirationInterval) { return }
|
if (!force && token == oldToken && System.currentTimeMillis() - lastUploadDate < tokenExpirationInterval) { return }
|
||||||
val parameters = mapOf( "token" to token, "pubKey" to hexEncodedPublicKey )
|
val parameters = mapOf( "token" to token, "pubKey" to publicKey )
|
||||||
val url = "$server/register"
|
val url = "$server/register"
|
||||||
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
||||||
val request = Request.Builder().url(url).post(body).build()
|
val request = Request.Builder().url(url).post(body).build()
|
||||||
|
@ -165,7 +165,7 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh
|
|||||||
}
|
}
|
||||||
val senderHexEncodedPublicKey = masterHexEncodedPublicKey ?: message.publicKey
|
val senderHexEncodedPublicKey = masterHexEncodedPublicKey ?: message.publicKey
|
||||||
val serviceDataMessage = getDataMessage(message)
|
val serviceDataMessage = getDataMessage(message)
|
||||||
val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false, false, false)
|
val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false, false)
|
||||||
if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) {
|
if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) {
|
||||||
PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
|
PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.protocol
|
|
||||||
|
|
||||||
import org.whispersystems.signalservice.internal.util.JsonUtil
|
|
||||||
|
|
||||||
class EphemeralMessage private constructor(val data: Map<*, *>) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun create(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey ))
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun createDeviceUnlinkingRequest(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey, "unpairingRequest" to true ))
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun createSessionRequest(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey, "friendRequest" to true, "sessionRequest" to true ))
|
|
||||||
|
|
||||||
internal fun parse(serialized: String): EphemeralMessage {
|
|
||||||
val data = JsonUtil.fromJson(serialized, Map::class.java) ?: throw IllegalArgumentException("Couldn't parse string to JSON")
|
|
||||||
return EphemeralMessage(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> get(key: String, defaultValue: T): T {
|
|
||||||
return data[key] as? T ?: defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
fun serialize(): String {
|
|
||||||
return JsonUtil.toJson(data)
|
|
||||||
}
|
|
||||||
}
|
|
@ -27,7 +27,7 @@ class MultiDeviceOpenGroupUpdateJob private constructor(parameters: Parameters)
|
|||||||
|
|
||||||
constructor() : this(Parameters.Builder()
|
constructor() : this(Parameters.Builder()
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
.addConstraint(NetworkConstraint.KEY)
|
||||||
.setQueue("MultiDeviceOpenGroupUpdateJob")
|
.setQueue(KEY)
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||||
.setMaxAttempts(Parameters.UNLIMITED)
|
.setMaxAttempts(Parameters.UNLIMITED)
|
||||||
.build())
|
.build())
|
||||||
|
@ -30,16 +30,16 @@ object MultiDeviceProtocol {
|
|||||||
enum class MessageType { Text, Media }
|
enum class MessageType { Text, Media }
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun sendTextPush(context: Context, recipient: Recipient, messageID: Long, isEndSession: Boolean) {
|
fun sendTextPush(context: Context, recipient: Recipient, messageID: Long) {
|
||||||
sendMessagePush(context, recipient, messageID, MessageType.Text, isEndSession)
|
sendMessagePush(context, recipient, messageID, MessageType.Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun sendMediaPush(context: Context, recipient: Recipient, messageID: Long) {
|
fun sendMediaPush(context: Context, recipient: Recipient, messageID: Long) {
|
||||||
sendMessagePush(context, recipient, messageID, MessageType.Media, false)
|
sendMessagePush(context, recipient, messageID, MessageType.Media)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendMessagePush(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType, isEndSession: Boolean) {
|
private fun sendMessagePush(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType) {
|
||||||
val jobManager = ApplicationContext.getInstance(context).jobManager
|
val jobManager = ApplicationContext.getInstance(context).jobManager
|
||||||
val isMultiDeviceRequired = !recipient.address.isOpenGroup
|
val isMultiDeviceRequired = !recipient.address.isOpenGroup
|
||||||
if (!isMultiDeviceRequired) {
|
if (!isMultiDeviceRequired) {
|
||||||
@ -162,6 +162,8 @@ object MultiDeviceProtocol {
|
|||||||
}
|
}
|
||||||
val isValid = isValidDeviceLinkMessage(context, deviceLink)
|
val isValid = isValidDeviceLinkMessage(context, deviceLink)
|
||||||
if (!isValid) { return }
|
if (!isValid) { return }
|
||||||
|
// The line below isn't actually necessary because this is called after PushDecryptJob
|
||||||
|
// calls handlePreKeyBundleMessageIfNeeded, but it also doesn't hurt.
|
||||||
SessionManagementProtocol.handlePreKeyBundleMessageIfNeeded(context, content)
|
SessionManagementProtocol.handlePreKeyBundleMessageIfNeeded(context, content)
|
||||||
linkingSession.processLinkingAuthorization(deviceLink)
|
linkingSession.processLinkingAuthorization(deviceLink)
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.protocol
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
|
||||||
import org.thoughtcrime.securesms.database.Address
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
|
||||||
import org.thoughtcrime.securesms.jobs.BaseJob
|
|
||||||
import org.thoughtcrime.securesms.logging.Log
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
|
||||||
import java.io.IOException
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
class PushEphemeralMessageSendJob private constructor(parameters: Parameters, private val message: EphemeralMessage) : BaseJob(parameters) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val KEY_MESSAGE = "message"
|
|
||||||
const val KEY = "PushBackgroundMessageSendJob"
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(message: EphemeralMessage) : this(Parameters.Builder()
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setQueue(KEY)
|
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
|
||||||
.setMaxAttempts(1)
|
|
||||||
.build(),
|
|
||||||
message)
|
|
||||||
|
|
||||||
override fun serialize(): Data {
|
|
||||||
return Data.Builder()
|
|
||||||
.putString(KEY_MESSAGE, message.serialize())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getFactoryKey(): String { return KEY }
|
|
||||||
|
|
||||||
public override fun onRun() {
|
|
||||||
val recipient = message.get<String?>("recipient", null) ?: throw IllegalStateException()
|
|
||||||
val dataMessage = SignalServiceDataMessage.newBuilder().withTimestamp(System.currentTimeMillis())
|
|
||||||
// Attach a pre key bundle if needed
|
|
||||||
if (message.get("friendRequest", false)) {
|
|
||||||
val bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(recipient)
|
|
||||||
dataMessage.withPreKeyBundle(bundle)
|
|
||||||
}
|
|
||||||
// Set flags if needed (these are mutually exclusive)
|
|
||||||
when {
|
|
||||||
message.get("unpairingRequest", false) -> dataMessage.asDeviceUnlinkingRequest(true)
|
|
||||||
message.get("sessionRequest", false) -> dataMessage.asSessionRequest(true)
|
|
||||||
}
|
|
||||||
// Send the message
|
|
||||||
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
|
||||||
val address = SignalServiceAddress(recipient)
|
|
||||||
try {
|
|
||||||
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(recipient), false))
|
|
||||||
messageSender.sendMessage(0, address, udAccess, dataMessage.build()) // The message ID doesn't matter
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.d("Loki", "Failed to send background message to: $recipient due to error: $e.")
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override fun onShouldRetry(e: Exception): Boolean {
|
|
||||||
// Disable since we have our own retrying
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCanceled() { }
|
|
||||||
|
|
||||||
class Factory : Job.Factory<PushEphemeralMessageSendJob> {
|
|
||||||
|
|
||||||
override fun create(parameters: Parameters, data: Data): PushEphemeralMessageSendJob {
|
|
||||||
try {
|
|
||||||
val messageJSON = data.getString(KEY_MESSAGE)
|
|
||||||
return PushEphemeralMessageSendJob(parameters, EphemeralMessage.parse(messageJSON))
|
|
||||||
} catch (e: IOException) {
|
|
||||||
throw AssertionError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,99 @@
|
|||||||
|
package org.thoughtcrime.securesms.loki.protocol
|
||||||
|
|
||||||
|
import com.google.protobuf.ByteString
|
||||||
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
||||||
|
import org.thoughtcrime.securesms.database.Address
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.Data
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.Job
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||||
|
import org.thoughtcrime.securesms.jobs.BaseJob
|
||||||
|
import org.thoughtcrime.securesms.logging.Log
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||||
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
||||||
|
import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities
|
||||||
|
import java.io.IOException
|
||||||
|
import java.security.SecureRandom
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class PushSessionRequestMessageSendJob private constructor(parameters: Parameters, private val publicKey: String) : BaseJob(parameters) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KEY = "PushSessionRequestMessageSendJob"
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(publicKey: String) : this(Parameters.Builder()
|
||||||
|
.addConstraint(NetworkConstraint.KEY)
|
||||||
|
.setQueue(KEY)
|
||||||
|
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||||
|
.setMaxAttempts(1)
|
||||||
|
.build(),
|
||||||
|
publicKey)
|
||||||
|
|
||||||
|
override fun serialize(): Data {
|
||||||
|
return Data.Builder().putString("publicKey", publicKey).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFactoryKey(): String { return KEY }
|
||||||
|
|
||||||
|
public override fun onRun() {
|
||||||
|
// Prepare
|
||||||
|
val contentMessage = SignalServiceProtos.Content.newBuilder()
|
||||||
|
// Attach the pre key bundle message
|
||||||
|
val preKeyBundleMessage = SignalServiceProtos.PreKeyBundleMessage.newBuilder()
|
||||||
|
val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(publicKey) ?: return
|
||||||
|
preKeyBundleMessage.identityKey = ByteString.copyFrom(preKeyBundle.identityKey.serialize())
|
||||||
|
preKeyBundleMessage.deviceId = preKeyBundle.deviceId
|
||||||
|
preKeyBundleMessage.preKeyId = preKeyBundle.preKeyId
|
||||||
|
preKeyBundleMessage.signedKeyId = preKeyBundle.signedPreKeyId
|
||||||
|
preKeyBundleMessage.preKey = ByteString.copyFrom(preKeyBundle.preKey.serialize())
|
||||||
|
preKeyBundleMessage.signedKey = ByteString.copyFrom(preKeyBundle.signedPreKey.serialize())
|
||||||
|
preKeyBundleMessage.signature = ByteString.copyFrom(preKeyBundle.signedPreKeySignature)
|
||||||
|
contentMessage.preKeyBundleMessage = preKeyBundleMessage.build()
|
||||||
|
// Attach the null message
|
||||||
|
val nullMessage = SignalServiceProtos.NullMessage.newBuilder()
|
||||||
|
val sr = SecureRandom()
|
||||||
|
val paddingSize = sr.nextInt(512)
|
||||||
|
val padding = ByteArray(paddingSize)
|
||||||
|
sr.nextBytes(padding)
|
||||||
|
nullMessage.padding = ByteString.copyFrom(padding)
|
||||||
|
contentMessage.nullMessage = nullMessage.build()
|
||||||
|
// Send the result
|
||||||
|
val serializedContentMessage = contentMessage.build().toByteArray()
|
||||||
|
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
||||||
|
val address = SignalServiceAddress(publicKey)
|
||||||
|
val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false)
|
||||||
|
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient)
|
||||||
|
val ttl = TTLUtilities.getTTL(TTLUtilities.MessageType.SessionRequest)
|
||||||
|
try {
|
||||||
|
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,
|
||||||
|
Date().time, serializedContentMessage, false, ttl, false,
|
||||||
|
true, false, false)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.d("Loki", "Failed to send session request to: $publicKey due to error: $e.")
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun onShouldRetry(e: Exception): Boolean {
|
||||||
|
// Disable since we have our own retrying
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCanceled() { }
|
||||||
|
|
||||||
|
class Factory : Job.Factory<PushSessionRequestMessageSendJob> {
|
||||||
|
|
||||||
|
override fun create(parameters: Parameters, data: Data): PushSessionRequestMessageSendJob {
|
||||||
|
try {
|
||||||
|
val publicKey = data.getString("publicKey")
|
||||||
|
return PushSessionRequestMessageSendJob(parameters, publicKey)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw AssertionError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,8 @@ import org.thoughtcrime.securesms.database.DatabaseFactory
|
|||||||
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob
|
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob
|
||||||
import org.thoughtcrime.securesms.loki.utilities.recipient
|
import org.thoughtcrime.securesms.loki.utilities.recipient
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.sms.MessageSender
|
||||||
|
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage
|
||||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage
|
import org.thoughtcrime.securesms.sms.OutgoingTextMessage
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
import org.whispersystems.libsignal.loki.SessionResetStatus
|
import org.whispersystems.libsignal.loki.SessionResetStatus
|
||||||
@ -27,8 +29,8 @@ object SessionManagementProtocol {
|
|||||||
val smsDB = DatabaseFactory.getSmsDatabase(context)
|
val smsDB = DatabaseFactory.getSmsDatabase(context)
|
||||||
val devices = lokiThreadDB.getSessionRestoreDevices(threadID)
|
val devices = lokiThreadDB.getSessionRestoreDevices(threadID)
|
||||||
for (device in devices) {
|
for (device in devices) {
|
||||||
val sessionRequest = EphemeralMessage.createSessionRequest(recipient.address.serialize())
|
val endSessionMessage = OutgoingEndSessionMessage(OutgoingTextMessage(recipient, "TERMINATE", 0, -1))
|
||||||
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(sessionRequest))
|
MessageSender.send(context, endSessionMessage, threadID, false, null)
|
||||||
}
|
}
|
||||||
val infoMessage = OutgoingTextMessage(recipient, "", 0, 0)
|
val infoMessage = OutgoingTextMessage(recipient, "", 0, 0)
|
||||||
val infoMessageID = smsDB.insertMessageOutbox(threadID, infoMessage, false, System.currentTimeMillis(), null)
|
val infoMessageID = smsDB.insertMessageOutbox(threadID, infoMessage, false, System.currentTimeMillis(), null)
|
||||||
@ -53,37 +55,24 @@ object SessionManagementProtocol {
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun handlePreKeyBundleMessageIfNeeded(context: Context, content: SignalServiceContent) {
|
fun handlePreKeyBundleMessageIfNeeded(context: Context, content: SignalServiceContent) {
|
||||||
val recipient = recipient(context, content.sender)
|
val publicKey = content.sender
|
||||||
if (recipient.isGroupRecipient) { return }
|
val recipient = recipient(context, publicKey)
|
||||||
val preKeyBundleMessage = content.lokiServiceMessage.orNull()?.preKeyBundleMessage ?: return
|
if (recipient.isGroupRecipient) { return } // Should never occur
|
||||||
|
val preKeyBundleMessage = content.preKeyBundleMessage.orNull() ?: return
|
||||||
val registrationID = TextSecurePreferences.getLocalRegistrationId(context) // TODO: It seems wrong to use the local registration ID for this?
|
val registrationID = TextSecurePreferences.getLocalRegistrationId(context) // TODO: It seems wrong to use the local registration ID for this?
|
||||||
val lokiPreKeyBundleDatabase = DatabaseFactory.getLokiPreKeyBundleDatabase(context)
|
val lokiPreKeyBundleDatabase = DatabaseFactory.getLokiPreKeyBundleDatabase(context)
|
||||||
Log.d("Loki", "Received a pre key bundle from: " + content.sender.toString() + ".")
|
Log.d("Loki", "Received a pre key bundle from: $publicKey.")
|
||||||
if (content.dataMessage.isPresent && content.dataMessage.get().isSessionRequest) {
|
|
||||||
val sessionRequestTimestamp = DatabaseFactory.getLokiAPIDatabase(context).getSessionRequestTimestamp(content.sender)
|
val sessionRequestTimestamp = DatabaseFactory.getLokiAPIDatabase(context).getSessionRequestTimestamp(content.sender)
|
||||||
if (sessionRequestTimestamp != null && content.timestamp < sessionRequestTimestamp) {
|
if (sessionRequestTimestamp != null && content.timestamp < sessionRequestTimestamp) {
|
||||||
// We sent or processed a session request after this one was sent
|
// We sent or processed a session request after this one was sent
|
||||||
Log.d("Loki", "Ignoring session request from: ${content.sender}.")
|
Log.d("Loki", "Ignoring session request from: ${content.sender}.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
val preKeyBundle = preKeyBundleMessage.getPreKeyBundle(registrationID)
|
val preKeyBundle = preKeyBundleMessage.getPreKeyBundle(registrationID)
|
||||||
lokiPreKeyBundleDatabase.setPreKeyBundle(content.sender, preKeyBundle)
|
lokiPreKeyBundleDatabase.setPreKeyBundle(content.sender, preKeyBundle)
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun handleSessionRequestIfNeeded(context: Context, content: SignalServiceContent): Boolean {
|
|
||||||
if (!content.dataMessage.isPresent || !content.dataMessage.get().isSessionRequest) { return false }
|
|
||||||
val sessionRequestTimestamp = DatabaseFactory.getLokiAPIDatabase(context).getSessionRequestTimestamp(content.sender)
|
|
||||||
if (sessionRequestTimestamp != null && content.timestamp < sessionRequestTimestamp) {
|
|
||||||
// We sent or processed a session request after this one was sent
|
|
||||||
Log.d("Loki", "Ignoring session request from: ${content.sender}.")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
DatabaseFactory.getLokiAPIDatabase(context).setSessionRequestTimestamp(content.sender, Date().time)
|
DatabaseFactory.getLokiAPIDatabase(context).setSessionRequestTimestamp(content.sender, Date().time)
|
||||||
val ephemeralMessage = EphemeralMessage.create(content.sender)
|
val job = PushNullMessageSendJob(content.sender)
|
||||||
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage))
|
ApplicationContext.getInstance(context).jobManager.add(job)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@ -95,8 +84,8 @@ object SessionManagementProtocol {
|
|||||||
sessionStore.archiveAllSessions(content.sender)
|
sessionStore.archiveAllSessions(content.sender)
|
||||||
lokiThreadDB.setSessionResetStatus(content.sender, SessionResetStatus.REQUEST_RECEIVED)
|
lokiThreadDB.setSessionResetStatus(content.sender, SessionResetStatus.REQUEST_RECEIVED)
|
||||||
Log.d("Loki", "Sending an ephemeral message back to: ${content.sender}.")
|
Log.d("Loki", "Sending an ephemeral message back to: ${content.sender}.")
|
||||||
val ephemeralMessage = EphemeralMessage.create(content.sender)
|
val job = PushNullMessageSendJob(content.sender)
|
||||||
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage))
|
ApplicationContext.getInstance(context).jobManager.add(job)
|
||||||
SecurityEvent.broadcastSecurityUpdateEvent(context)
|
SecurityEvent.broadcastSecurityUpdateEvent(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +19,8 @@ class SessionResetImplementation(private val context: Context) : SessionResetPro
|
|||||||
|
|
||||||
override fun onNewSessionAdopted(publicKey: String, oldSessionResetStatus: SessionResetStatus) {
|
override fun onNewSessionAdopted(publicKey: String, oldSessionResetStatus: SessionResetStatus) {
|
||||||
if (oldSessionResetStatus == SessionResetStatus.IN_PROGRESS) {
|
if (oldSessionResetStatus == SessionResetStatus.IN_PROGRESS) {
|
||||||
val ephemeralMessage = EphemeralMessage.create(publicKey)
|
val job = PushNullMessageSendJob(publicKey)
|
||||||
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage))
|
ApplicationContext.getInstance(context).jobManager.add(job)
|
||||||
}
|
}
|
||||||
// TODO: Show session reset succeed message
|
// TODO: Show session reset succeed message
|
||||||
}
|
}
|
||||||
|
@ -94,10 +94,8 @@ object SyncMessagesProtocol {
|
|||||||
val contactPublicKeys = contactsInputStream.readAll().map { it.number }
|
val contactPublicKeys = contactsInputStream.readAll().map { it.number }
|
||||||
for (contactPublicKey in contactPublicKeys) {
|
for (contactPublicKey in contactPublicKeys) {
|
||||||
if (contactPublicKey == userPublicKey || !PublicKeyValidation.isValid(contactPublicKey)) { return }
|
if (contactPublicKey == userPublicKey || !PublicKeyValidation.isValid(contactPublicKey)) { return }
|
||||||
val recipient = recipient(context, contactPublicKey)
|
|
||||||
val applicationContext = context.applicationContext as ApplicationContext
|
val applicationContext = context.applicationContext as ApplicationContext
|
||||||
applicationContext.sendSessionRequestIfNeeded(contactPublicKey)
|
applicationContext.sendSessionRequestIfNeeded(contactPublicKey)
|
||||||
// TODO: Make the thread visible
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class LokiRSSFeedPoller(private val context: Context, private val feed: LokiRSSF
|
|||||||
val id = feed.id.toByteArray()
|
val id = feed.id.toByteArray()
|
||||||
val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, SignalServiceGroup.GroupType.RSS_FEED, null, null, null, null)
|
val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, SignalServiceGroup.GroupType.RSS_FEED, null, null, null, null)
|
||||||
val x2 = SignalServiceDataMessage(timestamp, x1, null, body)
|
val x2 = SignalServiceDataMessage(timestamp, x1, null, body)
|
||||||
val x3 = SignalServiceContent(x2, "Loki", SignalServiceAddress.DEFAULT_DEVICE_ID, timestamp, false, false, false)
|
val x3 = SignalServiceContent(x2, "Loki", SignalServiceAddress.DEFAULT_DEVICE_ID, timestamp, false, false)
|
||||||
PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.absent())
|
PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.absent())
|
||||||
}
|
}
|
||||||
}.fail { exception ->
|
}.fail { exception ->
|
||||||
|
@ -28,22 +28,22 @@ object MentionUtilities {
|
|||||||
val mentions = mutableListOf<Tuple2<Range<Int>, String>>()
|
val mentions = mutableListOf<Tuple2<Range<Int>, String>>()
|
||||||
var startIndex = 0
|
var startIndex = 0
|
||||||
val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
||||||
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
if (matcher.find(startIndex)) {
|
if (matcher.find(startIndex)) {
|
||||||
while (true) {
|
while (true) {
|
||||||
val hexEncodedPublicKey = text.subSequence(matcher.start() + 1, matcher.end()).toString() // +1 to get rid of the @
|
val publicKey = text.subSequence(matcher.start() + 1, matcher.end()).toString() // +1 to get rid of the @
|
||||||
val userDisplayName: String? = if (hexEncodedPublicKey.toLowerCase() == userHexEncodedPublicKey.toLowerCase()) {
|
val userDisplayName: String? = if (publicKey.toLowerCase() == userPublicKey.toLowerCase()) {
|
||||||
TextSecurePreferences.getProfileName(context)
|
TextSecurePreferences.getProfileName(context)
|
||||||
} else if (publicChat != null) {
|
} else if (publicChat != null) {
|
||||||
DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.id, hexEncodedPublicKey)
|
DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.id, publicKey)
|
||||||
} else {
|
} else {
|
||||||
DatabaseFactory.getLokiUserDatabase(context).getDisplayName(hexEncodedPublicKey)
|
DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey)
|
||||||
}
|
}
|
||||||
if (userDisplayName != null) {
|
if (userDisplayName != null) {
|
||||||
text = text.subSequence(0, matcher.start()).toString() + "@" + userDisplayName + text.subSequence(matcher.end(), text.length)
|
text = text.subSequence(0, matcher.start()).toString() + "@" + userDisplayName + text.subSequence(matcher.end(), text.length)
|
||||||
val endIndex = matcher.start() + 1 + userDisplayName.length
|
val endIndex = matcher.start() + 1 + userDisplayName.length
|
||||||
startIndex = endIndex
|
startIndex = endIndex
|
||||||
mentions.add(Tuple2(Range.create(matcher.start(), endIndex), hexEncodedPublicKey))
|
mentions.add(Tuple2(Range.create(matcher.start(), endIndex), publicKey))
|
||||||
} else {
|
} else {
|
||||||
startIndex = matcher.end()
|
startIndex = matcher.end()
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ public class MessageSender {
|
|||||||
|
|
||||||
long messageId = database.insertMessageOutbox(allocatedThreadId, message, forceSms, System.currentTimeMillis(), insertListener);
|
long messageId = database.insertMessageOutbox(allocatedThreadId, message, forceSms, System.currentTimeMillis(), insertListener);
|
||||||
|
|
||||||
sendTextMessage(context, recipient, forceSms, keyExchange, messageId, message.isEndSession());
|
sendTextMessage(context, recipient, forceSms, keyExchange, messageId);
|
||||||
|
|
||||||
return allocatedThreadId;
|
return allocatedThreadId;
|
||||||
}
|
}
|
||||||
@ -124,7 +124,7 @@ public class MessageSender {
|
|||||||
if (messageRecord.isMms()) {
|
if (messageRecord.isMms()) {
|
||||||
sendMediaMessage(context, recipient, forceSms, messageId, expiresIn);
|
sendMediaMessage(context, recipient, forceSms, messageId, expiresIn);
|
||||||
} else {
|
} else {
|
||||||
sendTextMessage(context, recipient, forceSms, keyExchange, messageId, messageRecord.isEndSession());
|
sendTextMessage(context, recipient, forceSms, keyExchange, messageId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,17 +141,17 @@ public class MessageSender {
|
|||||||
|
|
||||||
private static void sendTextMessage(Context context, Recipient recipient,
|
private static void sendTextMessage(Context context, Recipient recipient,
|
||||||
boolean forceSms, boolean keyExchange,
|
boolean forceSms, boolean keyExchange,
|
||||||
long messageId, boolean isEndSession)
|
long messageId)
|
||||||
{
|
{
|
||||||
if (isLocalSelfSend(context, recipient, forceSms)) {
|
if (isLocalSelfSend(context, recipient, forceSms)) {
|
||||||
sendLocalTextSelf(context, messageId);
|
sendLocalTextSelf(context, messageId);
|
||||||
} else {
|
} else {
|
||||||
sendTextPush(context, recipient, messageId, isEndSession);
|
sendTextPush(context, recipient, messageId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendTextPush(Context context, Recipient recipient, long messageId, boolean isEndSession) {
|
private static void sendTextPush(Context context, Recipient recipient, long messageId) {
|
||||||
MultiDeviceProtocol.sendTextPush(context, recipient, messageId, isEndSession);
|
MultiDeviceProtocol.sendTextPush(context, recipient, messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendMediaPush(Context context, Recipient recipient, long messageId) {
|
private static void sendMediaPush(Context context, Recipient recipient, long messageId) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user