mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 20:15:21 +00:00
Message syncing.
This commit is contained in:
parent
98cfd93b97
commit
efad14fcdc
@ -20,13 +20,17 @@ import android.annotation.SuppressLint;
|
|||||||
import android.arch.lifecycle.DefaultLifecycleObserver;
|
import android.arch.lifecycle.DefaultLifecycleObserver;
|
||||||
import android.arch.lifecycle.LifecycleOwner;
|
import android.arch.lifecycle.LifecycleOwner;
|
||||||
import android.arch.lifecycle.ProcessLifecycleOwner;
|
import android.arch.lifecycle.ProcessLifecycleOwner;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.multidex.MultiDexApplication;
|
import android.support.multidex.MultiDexApplication;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
|
||||||
import com.crashlytics.android.Crashlytics;
|
import com.crashlytics.android.Crashlytics;
|
||||||
import com.google.android.gms.security.ProviderInstaller;
|
import com.google.android.gms.security.ProviderInstaller;
|
||||||
@ -62,6 +66,7 @@ import org.thoughtcrime.securesms.logging.PersistentLogger;
|
|||||||
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
|
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
|
||||||
import org.thoughtcrime.securesms.loki.BackgroundPollWorker;
|
import org.thoughtcrime.securesms.loki.BackgroundPollWorker;
|
||||||
import org.thoughtcrime.securesms.loki.LokiAPIDatabase;
|
import org.thoughtcrime.securesms.loki.LokiAPIDatabase;
|
||||||
|
import org.thoughtcrime.securesms.loki.LokiMessageSyncEvent;
|
||||||
import org.thoughtcrime.securesms.loki.LokiPublicChatManager;
|
import org.thoughtcrime.securesms.loki.LokiPublicChatManager;
|
||||||
import org.thoughtcrime.securesms.loki.LokiRSSFeedPoller;
|
import org.thoughtcrime.securesms.loki.LokiRSSFeedPoller;
|
||||||
import org.thoughtcrime.securesms.loki.LokiUserDatabase;
|
import org.thoughtcrime.securesms.loki.LokiUserDatabase;
|
||||||
@ -77,6 +82,7 @@ import org.thoughtcrime.securesms.service.LocalBackupListener;
|
|||||||
import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
|
import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
|
||||||
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
|
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
|
||||||
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
|
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
|
||||||
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
|
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
|
||||||
import org.webrtc.PeerConnectionFactory;
|
import org.webrtc.PeerConnectionFactory;
|
||||||
@ -141,6 +147,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
private LokiPublicChatAPI lokiPublicChatAPI = null;
|
private LokiPublicChatAPI lokiPublicChatAPI = null;
|
||||||
public SignalCommunicationModule communicationModule;
|
public SignalCommunicationModule communicationModule;
|
||||||
public MixpanelAPI mixpanel;
|
public MixpanelAPI mixpanel;
|
||||||
|
private BroadcastReceiver syncMessageEventReceiver;
|
||||||
|
|
||||||
private volatile boolean isAppVisible;
|
private volatile boolean isAppVisible;
|
||||||
|
|
||||||
@ -193,6 +200,22 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
if (setUpStorageAPIIfNeeded()) {
|
if (setUpStorageAPIIfNeeded()) {
|
||||||
LokiStorageAPI.Companion.getShared().updateUserDeviceMappings();
|
LokiStorageAPI.Companion.getShared().updateUserDeviceMappings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loki - Event listener
|
||||||
|
syncMessageEventReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
// Send the sync message to our devices
|
||||||
|
long messageID = intent.getLongExtra(LokiMessageSyncEvent.MESSAGE_ID, -1);
|
||||||
|
long timestamp = intent.getLongExtra(LokiMessageSyncEvent.TIMESTAMP, -1);
|
||||||
|
byte[] message = intent.getByteArrayExtra(LokiMessageSyncEvent.SYNC_MESSAGE);
|
||||||
|
int ttl = intent.getIntExtra(LokiMessageSyncEvent.TTL, -1);
|
||||||
|
if (messageID > 0 && timestamp > 0 && message != null && ttl > 0) {
|
||||||
|
MessageSender.sendSyncMessageToOurDevices(context, messageID, timestamp, message, ttl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
LocalBroadcastManager.getInstance(this).registerReceiver(syncMessageEventReceiver, new IntentFilter(LokiMessageSyncEvent.MESSAGE_SYNC_EVENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -220,6 +243,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
@Override
|
@Override
|
||||||
public void onTerminate() {
|
public void onTerminate() {
|
||||||
stopKovenant();
|
stopKovenant();
|
||||||
|
LocalBroadcastManager.getInstance(this).unregisterReceiver(syncMessageEventReceiver);
|
||||||
super.onTerminate();
|
super.onTerminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,8 +47,9 @@ import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
|
|||||||
import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob;
|
||||||
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
||||||
import org.thoughtcrime.securesms.push.SecurityEventListener;
|
import org.thoughtcrime.securesms.push.MessageSenderEventListener;
|
||||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||||
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
|
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
|
||||||
import org.thoughtcrime.securesms.service.WebRtcCallService;
|
import org.thoughtcrime.securesms.service.WebRtcCallService;
|
||||||
@ -112,7 +113,8 @@ import network.loki.messenger.BuildConfig;
|
|||||||
StickerPackDownloadJob.class,
|
StickerPackDownloadJob.class,
|
||||||
MultiDeviceStickerPackOperationJob.class,
|
MultiDeviceStickerPackOperationJob.class,
|
||||||
MultiDeviceStickerPackSyncJob.class,
|
MultiDeviceStickerPackSyncJob.class,
|
||||||
LinkPreviewRepository.class})
|
LinkPreviewRepository.class,
|
||||||
|
PushMessageSyncSendJob.class})
|
||||||
|
|
||||||
public class SignalCommunicationModule {
|
public class SignalCommunicationModule {
|
||||||
|
|
||||||
@ -151,7 +153,7 @@ public class SignalCommunicationModule {
|
|||||||
TextSecurePreferences.isMultiDevice(context),
|
TextSecurePreferences.isMultiDevice(context),
|
||||||
Optional.fromNullable(IncomingMessageObserver.getPipe()),
|
Optional.fromNullable(IncomingMessageObserver.getPipe()),
|
||||||
Optional.fromNullable(IncomingMessageObserver.getUnidentifiedPipe()),
|
Optional.fromNullable(IncomingMessageObserver.getUnidentifiedPipe()),
|
||||||
Optional.of(new SecurityEventListener(context)),
|
Optional.of(new MessageSenderEventListener(context)),
|
||||||
TextSecurePreferences.getLocalNumber(context),
|
TextSecurePreferences.getLocalNumber(context),
|
||||||
DatabaseFactory.getLokiAPIDatabase(context),
|
DatabaseFactory.getLokiAPIDatabase(context),
|
||||||
DatabaseFactory.getLokiThreadDatabase(context),
|
DatabaseFactory.getLokiThreadDatabase(context),
|
||||||
|
@ -24,6 +24,7 @@ public class Data {
|
|||||||
@JsonProperty private final Map<String, double[]> doubleArrays;
|
@JsonProperty private final Map<String, double[]> doubleArrays;
|
||||||
@JsonProperty private final Map<String, Boolean> booleans;
|
@JsonProperty private final Map<String, Boolean> booleans;
|
||||||
@JsonProperty private final Map<String, boolean[]> booleanArrays;
|
@JsonProperty private final Map<String, boolean[]> booleanArrays;
|
||||||
|
@JsonProperty private final Map<String, byte[]> byteArrays;
|
||||||
|
|
||||||
public Data(@JsonProperty("strings") @NonNull Map<String, String> strings,
|
public Data(@JsonProperty("strings") @NonNull Map<String, String> strings,
|
||||||
@JsonProperty("stringArrays") @NonNull Map<String, String[]> stringArrays,
|
@JsonProperty("stringArrays") @NonNull Map<String, String[]> stringArrays,
|
||||||
@ -36,7 +37,8 @@ public class Data {
|
|||||||
@JsonProperty("doubles") @NonNull Map<String, Double> doubles,
|
@JsonProperty("doubles") @NonNull Map<String, Double> doubles,
|
||||||
@JsonProperty("doubleArrays") @NonNull Map<String, double[]> doubleArrays,
|
@JsonProperty("doubleArrays") @NonNull Map<String, double[]> doubleArrays,
|
||||||
@JsonProperty("booleans") @NonNull Map<String, Boolean> booleans,
|
@JsonProperty("booleans") @NonNull Map<String, Boolean> booleans,
|
||||||
@JsonProperty("booleanArrays") @NonNull Map<String, boolean[]> booleanArrays)
|
@JsonProperty("booleanArrays") @NonNull Map<String, boolean[]> booleanArrays,
|
||||||
|
@JsonProperty("byteArrays") @NonNull Map<String, byte[]> byteArrays)
|
||||||
{
|
{
|
||||||
this.strings = strings;
|
this.strings = strings;
|
||||||
this.stringArrays = stringArrays;
|
this.stringArrays = stringArrays;
|
||||||
@ -50,6 +52,7 @@ public class Data {
|
|||||||
this.doubleArrays = doubleArrays;
|
this.doubleArrays = doubleArrays;
|
||||||
this.booleans = booleans;
|
this.booleans = booleans;
|
||||||
this.booleanArrays = booleanArrays;
|
this.booleanArrays = booleanArrays;
|
||||||
|
this.byteArrays = byteArrays;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasString(@NonNull String key) {
|
public boolean hasString(@NonNull String key) {
|
||||||
@ -201,6 +204,14 @@ public class Data {
|
|||||||
return booleanArrays.get(key);
|
return booleanArrays.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasByteArray(@NonNull String key) {
|
||||||
|
return byteArrays.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getByteArray(@NonNull String key) {
|
||||||
|
throwIfAbsent(byteArrays, key);
|
||||||
|
return byteArrays.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
private void throwIfAbsent(@NonNull Map map, @NonNull String key) {
|
private void throwIfAbsent(@NonNull Map map, @NonNull String key) {
|
||||||
if (!map.containsKey(key)) {
|
if (!map.containsKey(key)) {
|
||||||
@ -223,6 +234,7 @@ public class Data {
|
|||||||
private final Map<String, double[]> doubleArrays = new HashMap<>();
|
private final Map<String, double[]> doubleArrays = new HashMap<>();
|
||||||
private final Map<String, Boolean> booleans = new HashMap<>();
|
private final Map<String, Boolean> booleans = new HashMap<>();
|
||||||
private final Map<String, boolean[]> booleanArrays = new HashMap<>();
|
private final Map<String, boolean[]> booleanArrays = new HashMap<>();
|
||||||
|
private final Map<String, byte[]> byteArrays = new HashMap<>();
|
||||||
|
|
||||||
public Builder putString(@NonNull String key, @Nullable String value) {
|
public Builder putString(@NonNull String key, @Nullable String value) {
|
||||||
strings.put(key, value);
|
strings.put(key, value);
|
||||||
@ -284,6 +296,11 @@ public class Data {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder putByteArray(@NonNull String key, @NonNull byte[] value) {
|
||||||
|
byteArrays.put(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Data build() {
|
public Data build() {
|
||||||
return new Data(strings,
|
return new Data(strings,
|
||||||
stringArrays,
|
stringArrays,
|
||||||
@ -296,7 +313,8 @@ public class Data {
|
|||||||
doubles,
|
doubles,
|
||||||
doubleArrays,
|
doubleArrays,
|
||||||
booleans,
|
booleans,
|
||||||
booleanArrays);
|
booleanArrays,
|
||||||
|
byteArrays);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver;
|
|||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
|
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.PushMessageSyncSendJob;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -70,6 +71,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(PushMessageSyncSendJob.KEY, new PushMessageSyncSendJob.Factory());
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -822,7 +822,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
|
|
||||||
private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message) throws MmsException {
|
private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message) throws MmsException {
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||||
Recipient recipient = getSyncMessageDestination(message);
|
Recipient recipient = getSyncMessagePrimaryDestination(message);
|
||||||
|
|
||||||
OutgoingExpirationUpdateMessage expirationUpdateMessage = new OutgoingExpirationUpdateMessage(recipient,
|
OutgoingExpirationUpdateMessage expirationUpdateMessage = new OutgoingExpirationUpdateMessage(recipient,
|
||||||
message.getTimestamp(),
|
message.getTimestamp(),
|
||||||
@ -842,7 +842,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
throws MmsException
|
throws MmsException
|
||||||
{
|
{
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||||
Recipient recipients = getSyncMessageDestination(message);
|
Recipient recipients = getSyncMessagePrimaryDestination(message);
|
||||||
Optional<QuoteModel> quote = getValidatedQuote(message.getMessage().getQuote());
|
Optional<QuoteModel> quote = getValidatedQuote(message.getMessage().getQuote());
|
||||||
Optional<Attachment> sticker = getStickerAttachment(message.getMessage().getSticker());
|
Optional<Attachment> sticker = getStickerAttachment(message.getMessage().getSticker());
|
||||||
Optional<List<Contact>> sharedContacts = getContacts(message.getMessage().getSharedContacts());
|
Optional<List<Contact>> sharedContacts = getContacts(message.getMessage().getSharedContacts());
|
||||||
@ -1172,7 +1172,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
throws MmsException
|
throws MmsException
|
||||||
{
|
{
|
||||||
|
|
||||||
Recipient recipient = getSyncMessageDestination(message);
|
Recipient recipient = getSyncMessagePrimaryDestination(message);
|
||||||
String body = message.getMessage().getBody().or("");
|
String body = message.getMessage().getBody().or("");
|
||||||
long expiresInMillis = message.getMessage().getExpiresInSeconds() * 1000L;
|
long expiresInMillis = message.getMessage().getExpiresInSeconds() * 1000L;
|
||||||
|
|
||||||
@ -1523,35 +1523,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Recipient getMessagePrimaryDestination(SignalServiceContent content, SignalServiceDataMessage message) {
|
private Recipient getSyncMessagePrimaryDestination(SentTranscriptMessage message) {
|
||||||
if (message.getGroupInfo().isPresent()) {
|
if (message.getMessage().getGroupInfo().isPresent()) {
|
||||||
return Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false)), false);
|
return getSyncMessageDestination(message);
|
||||||
} else {
|
} else {
|
||||||
SettableFuture<String> device = new SettableFuture<>();
|
return getPrimaryDeviceRecipient(message.getDestination().get());
|
||||||
String contentSender = content.getSender();
|
|
||||||
|
|
||||||
// Get the primary device
|
|
||||||
LokiStorageAPI.shared.getPrimaryDevicePublicKey(contentSender).success(primaryDevice -> {
|
|
||||||
String publicKey = (primaryDevice != null) ? primaryDevice : contentSender;
|
|
||||||
// If our the public key matches our primary device then we need to forward the message to ourselves (Note to self)
|
|
||||||
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
|
||||||
if (ourPrimaryDevice != null && ourPrimaryDevice.equals(publicKey)) {
|
|
||||||
publicKey = TextSecurePreferences.getLocalNumber(context);
|
|
||||||
}
|
|
||||||
device.set(publicKey);
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}).fail(exception -> {
|
|
||||||
device.set(contentSender);
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
String primarySender = device.get();
|
|
||||||
return Recipient.from(context, Address.fromSerialized(primarySender), false);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.d("Loki", "Failed to get primary device public key for message. " + e.getMessage());
|
|
||||||
return Recipient.from(context, Address.fromSerialized(content.getSender()), false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1563,6 +1539,41 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Recipient getMessagePrimaryDestination(SignalServiceContent content, SignalServiceDataMessage message) {
|
||||||
|
if (message.getGroupInfo().isPresent()) {
|
||||||
|
return getMessageDestination(content, message);
|
||||||
|
} else {
|
||||||
|
return getPrimaryDeviceRecipient(content.getSender());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Recipient getPrimaryDeviceRecipient(String recipient) {
|
||||||
|
SettableFuture<String> device = new SettableFuture<>();
|
||||||
|
|
||||||
|
// Get the primary device
|
||||||
|
LokiStorageAPI.shared.getPrimaryDevicePublicKey(recipient).success(primaryDevice -> {
|
||||||
|
String publicKey = (primaryDevice != null) ? primaryDevice : recipient;
|
||||||
|
// If our the public key matches our primary device then we need to forward the message to ourselves (Note to self)
|
||||||
|
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
||||||
|
if (ourPrimaryDevice != null && ourPrimaryDevice.equals(publicKey)) {
|
||||||
|
publicKey = TextSecurePreferences.getLocalNumber(context);
|
||||||
|
}
|
||||||
|
device.set(publicKey);
|
||||||
|
return Unit.INSTANCE;
|
||||||
|
}).fail(exception -> {
|
||||||
|
device.set(recipient);
|
||||||
|
return Unit.INSTANCE;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
String primarySender = device.get();
|
||||||
|
return Recipient.from(context, Address.fromSerialized(primarySender), false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d("Loki", "Failed to get primary device public key for message. " + e.getMessage());
|
||||||
|
return Recipient.from(context, Address.fromSerialized(recipient), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient conversationRecipient, @NonNull String sender, int device) {
|
private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient conversationRecipient, @NonNull String sender, int device) {
|
||||||
Recipient author = Recipient.from(context, Address.fromSerialized(sender), false);
|
Recipient author = Recipient.from(context, Address.fromSerialized(sender), false);
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(conversationRecipient);
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(conversationRecipient);
|
||||||
@ -1609,6 +1620,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
} else if (content.getCallMessage().isPresent() || content.getTypingMessage().isPresent()) {
|
} else if (content.getCallMessage().isPresent() || content.getTypingMessage().isPresent()) {
|
||||||
return sender.isBlocked();
|
return sender.isBlocked();
|
||||||
|
} else if (content.getSyncMessage().isPresent()) {
|
||||||
|
// We should ignore a sync message if the sender is not one of our devices
|
||||||
|
boolean isOurDevice = MultiDeviceUtilitiesKt.isOneOfOurDevices(context, sender.getAddress());
|
||||||
|
if (!isOurDevice) { Log.w(TAG, "Got a sync message from a device that is not ours!."); }
|
||||||
|
return !isOurDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.jobmanager.Data;
|
|||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.loki.MultiDeviceUtilitiesKt;
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
@ -42,6 +43,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSy
|
|||||||
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
||||||
|
import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -61,6 +63,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
|||||||
private static final String KEY_DESTINATION = "destination";
|
private static final String KEY_DESTINATION = "destination";
|
||||||
private static final String KEY_IS_FRIEND_REQUEST = "is_friend_request";
|
private static final String KEY_IS_FRIEND_REQUEST = "is_friend_request";
|
||||||
private static final String KEY_CUSTOM_FR_MESSAGE = "custom_friend_request_message";
|
private static final String KEY_CUSTOM_FR_MESSAGE = "custom_friend_request_message";
|
||||||
|
private static final String KEY_SHOULD_SEND_SYNC_MESSAGE = "should_send_sync_message";
|
||||||
|
|
||||||
@Inject SignalServiceMessageSender messageSender;
|
@Inject SignalServiceMessageSender messageSender;
|
||||||
|
|
||||||
@ -71,34 +74,36 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
|||||||
private Address destination; // Destination to check whether this is another device we're sending to
|
private Address destination; // Destination to check whether this is another device we're sending to
|
||||||
private boolean isFriendRequest; // Whether this is a friend request message
|
private boolean isFriendRequest; // Whether this is a friend request message
|
||||||
private String customFriendRequestMessage; // If this isn't set then we use the message body
|
private String customFriendRequestMessage; // If this isn't set then we use the message body
|
||||||
|
private boolean shouldSendSyncMessage;
|
||||||
|
|
||||||
public PushMediaSendJob(long messageId, Address destination) { this(messageId, messageId, destination); }
|
public PushMediaSendJob(long messageId, Address destination) { this(messageId, messageId, destination); }
|
||||||
public PushMediaSendJob(long templateMessageId, long messageId, Address destination) { this(templateMessageId, messageId, destination, false, null); }
|
public PushMediaSendJob(long templateMessageId, long messageId, Address destination) { this(templateMessageId, messageId, destination, false, null, false); }
|
||||||
public PushMediaSendJob(long templateMessageId, long messageId, Address destination, boolean isFriendRequest, String customFriendRequestMessage) {
|
public PushMediaSendJob(long templateMessageId, long messageId, Address destination, boolean isFriendRequest, String customFriendRequestMessage, boolean shouldSendSyncMessage) {
|
||||||
this(constructParameters(destination), templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage);
|
this(constructParameters(destination), templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage, shouldSendSyncMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PushMediaSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination, boolean isFriendRequest, String customFriendRequestMessage) {
|
private PushMediaSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination, boolean isFriendRequest, String customFriendRequestMessage, boolean shouldSendSyncMessage) {
|
||||||
super(parameters);
|
super(parameters);
|
||||||
this.templateMessageId = templateMessageId;
|
this.templateMessageId = templateMessageId;
|
||||||
this.messageId = messageId;
|
this.messageId = messageId;
|
||||||
this.destination = destination;
|
this.destination = destination;
|
||||||
this.isFriendRequest = isFriendRequest;
|
this.isFriendRequest = isFriendRequest;
|
||||||
this.customFriendRequestMessage = customFriendRequestMessage;
|
this.customFriendRequestMessage = customFriendRequestMessage;
|
||||||
|
this.shouldSendSyncMessage = shouldSendSyncMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Address destination) {
|
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Address destination, boolean shouldSendSyncMessage) {
|
||||||
enqueue(context, jobManager, messageId, messageId, destination);
|
enqueue(context, jobManager, messageId, messageId, destination, shouldSendSyncMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long templateMessageId, long messageId, @NonNull Address destination) {
|
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long templateMessageId, long messageId, @NonNull Address destination, boolean shouldSendSyncMessage) {
|
||||||
enqueue(context, jobManager, templateMessageId, messageId, destination, false, null);
|
enqueue(context, jobManager, templateMessageId, messageId, destination, false, null, shouldSendSyncMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long templateMessageId, long messageId, @NonNull Address destination, Boolean isFriendRequest, @Nullable String customFriendRequestMessage) {
|
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long templateMessageId, long messageId, @NonNull Address destination, Boolean isFriendRequest, @Nullable String customFriendRequestMessage, boolean shouldSendSyncMessage) {
|
||||||
try {
|
try {
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||||
OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
|
OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
|
||||||
@ -111,10 +116,10 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
|||||||
List<AttachmentUploadJob> attachmentJobs = Stream.of(attachments).map(a -> new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId(), destination)).toList();
|
List<AttachmentUploadJob> attachmentJobs = Stream.of(attachments).map(a -> new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId(), destination)).toList();
|
||||||
|
|
||||||
if (attachmentJobs.isEmpty()) {
|
if (attachmentJobs.isEmpty()) {
|
||||||
jobManager.add(new PushMediaSendJob(templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage));
|
jobManager.add(new PushMediaSendJob(templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage, shouldSendSyncMessage));
|
||||||
} else {
|
} else {
|
||||||
jobManager.startChain(attachmentJobs)
|
jobManager.startChain(attachmentJobs)
|
||||||
.then(new PushMediaSendJob(templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage))
|
.then(new PushMediaSendJob(templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage, shouldSendSyncMessage))
|
||||||
.enqueue();
|
.enqueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +136,8 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
|||||||
.putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId)
|
.putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId)
|
||||||
.putLong(KEY_MESSAGE_ID, messageId)
|
.putLong(KEY_MESSAGE_ID, messageId)
|
||||||
.putString(KEY_DESTINATION, destination.serialize())
|
.putString(KEY_DESTINATION, destination.serialize())
|
||||||
.putBoolean(KEY_IS_FRIEND_REQUEST, isFriendRequest);
|
.putBoolean(KEY_IS_FRIEND_REQUEST, isFriendRequest)
|
||||||
|
.putBoolean(KEY_SHOULD_SEND_SYNC_MESSAGE, shouldSendSyncMessage);
|
||||||
|
|
||||||
if (customFriendRequestMessage != null) { builder.putString(KEY_CUSTOM_FR_MESSAGE, customFriendRequestMessage); }
|
if (customFriendRequestMessage != null) { builder.putString(KEY_CUSTOM_FR_MESSAGE, customFriendRequestMessage); }
|
||||||
return builder.build();
|
return builder.build();
|
||||||
@ -271,7 +277,15 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
|||||||
messageSender.sendMessage(messageId, syncMessage, syncAccess);
|
messageSender.sendMessage(messageId, syncMessage, syncAccess);
|
||||||
return syncAccess.isPresent();
|
return syncAccess.isPresent();
|
||||||
} else {
|
} else {
|
||||||
return messageSender.sendMessage(messageId, address, UnidentifiedAccessUtil.getAccessFor(context, recipient), mediaMessage).getSuccess().isUnidentified();
|
LokiSyncMessage syncMessage = null;
|
||||||
|
if (shouldSendSyncMessage) {
|
||||||
|
// Set the sync message destination the primary device, this way it will show that we sent a message to the primary device and not a secondary device
|
||||||
|
String primaryDevice = MultiDeviceUtilitiesKt.getPrimaryDevicePublicKey(address.getNumber());
|
||||||
|
SignalServiceAddress primaryAddress = primaryDevice == null ? address : new SignalServiceAddress(primaryDevice);
|
||||||
|
// We also need to use the original message id and not -1
|
||||||
|
syncMessage = new LokiSyncMessage(primaryAddress, templateMessageId);
|
||||||
|
}
|
||||||
|
return messageSender.sendMessage(messageId, address, UnidentifiedAccessUtil.getAccessFor(context, recipient), mediaMessage, Optional.fromNullable(syncMessage)).getSuccess().isUnidentified();
|
||||||
}
|
}
|
||||||
} catch (UnregisteredUserException e) {
|
} catch (UnregisteredUserException e) {
|
||||||
warn(TAG, e);
|
warn(TAG, e);
|
||||||
@ -292,8 +306,9 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
|||||||
long messageID = data.getLong(KEY_MESSAGE_ID);
|
long messageID = data.getLong(KEY_MESSAGE_ID);
|
||||||
Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION));
|
Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION));
|
||||||
boolean isFriendRequest = data.getBoolean(KEY_IS_FRIEND_REQUEST);
|
boolean isFriendRequest = data.getBoolean(KEY_IS_FRIEND_REQUEST);
|
||||||
|
boolean shouldSendSyncMessage = data.getBoolean(KEY_SHOULD_SEND_SYNC_MESSAGE);
|
||||||
String frMessage = data.hasString(KEY_CUSTOM_FR_MESSAGE) ? data.getString(KEY_CUSTOM_FR_MESSAGE) : null;
|
String frMessage = data.hasString(KEY_CUSTOM_FR_MESSAGE) ? data.getString(KEY_CUSTOM_FR_MESSAGE) : null;
|
||||||
return new PushMediaSendJob(parameters, templateMessageID, messageID, destination, isFriendRequest, frMessage);
|
return new PushMediaSendJob(parameters, templateMessageID, messageID, destination, isFriendRequest, frMessage, shouldSendSyncMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
|||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
|
import org.thoughtcrime.securesms.loki.MultiDeviceUtilitiesKt;
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||||
@ -29,6 +30,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
|||||||
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
||||||
|
import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@ -45,6 +47,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
|||||||
private static final String KEY_DESTINATION = "destination";
|
private static final String KEY_DESTINATION = "destination";
|
||||||
private static final String KEY_IS_FRIEND_REQUEST = "is_friend_request";
|
private static final String KEY_IS_FRIEND_REQUEST = "is_friend_request";
|
||||||
private static final String KEY_CUSTOM_FR_MESSAGE = "custom_friend_request_message";
|
private static final String KEY_CUSTOM_FR_MESSAGE = "custom_friend_request_message";
|
||||||
|
private static final String KEY_SHOULD_SEND_SYNC_MESSAGE = "should_send_sync_message";
|
||||||
|
|
||||||
@Inject SignalServiceMessageSender messageSender;
|
@Inject SignalServiceMessageSender messageSender;
|
||||||
|
|
||||||
@ -55,20 +58,22 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
|||||||
private Address destination; // Destination to check whether this is another device we're sending to
|
private Address destination; // Destination to check whether this is another device we're sending to
|
||||||
private boolean isFriendRequest; // Whether this is a friend request message
|
private boolean isFriendRequest; // Whether this is a friend request message
|
||||||
private String customFriendRequestMessage; // If this isn't set then we use the message body
|
private String customFriendRequestMessage; // If this isn't set then we use the message body
|
||||||
|
private boolean shouldSendSyncMessage;
|
||||||
|
|
||||||
public PushTextSendJob(long messageId, Address destination) { this(messageId, messageId, destination); }
|
public PushTextSendJob(long messageId, Address destination) { this(messageId, messageId, destination, false); }
|
||||||
public PushTextSendJob(long templateMessageId, long messageId, Address destination) { this(templateMessageId, messageId, destination, false, null); }
|
public PushTextSendJob(long templateMessageId, long messageId, Address destination, boolean shouldSendSyncMessage) { this(templateMessageId, messageId, destination, false, null, shouldSendSyncMessage); }
|
||||||
public PushTextSendJob(long templateMessageId, long messageId, Address destination, boolean isFriendRequest, String customFriendRequestMessage) {
|
public PushTextSendJob(long templateMessageId, long messageId, Address destination, boolean isFriendRequest, String customFriendRequestMessage, boolean shouldSendSyncMessage) {
|
||||||
this(constructParameters(destination), templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage);
|
this(constructParameters(destination), templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage, shouldSendSyncMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PushTextSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination, boolean isFriendRequest, String customFriendRequestMessage) {
|
private PushTextSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination, boolean isFriendRequest, String customFriendRequestMessage, boolean shouldSendSyncMessage) {
|
||||||
super(parameters);
|
super(parameters);
|
||||||
this.templateMessageId = templateMessageId;
|
this.templateMessageId = templateMessageId;
|
||||||
this.messageId = messageId;
|
this.messageId = messageId;
|
||||||
this.destination = destination;
|
this.destination = destination;
|
||||||
this.isFriendRequest = isFriendRequest;
|
this.isFriendRequest = isFriendRequest;
|
||||||
this.customFriendRequestMessage = customFriendRequestMessage;
|
this.customFriendRequestMessage = customFriendRequestMessage;
|
||||||
|
this.shouldSendSyncMessage = shouldSendSyncMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -77,7 +82,8 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
|||||||
.putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId)
|
.putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId)
|
||||||
.putLong(KEY_MESSAGE_ID, messageId)
|
.putLong(KEY_MESSAGE_ID, messageId)
|
||||||
.putString(KEY_DESTINATION, destination.serialize())
|
.putString(KEY_DESTINATION, destination.serialize())
|
||||||
.putBoolean(KEY_IS_FRIEND_REQUEST, isFriendRequest);
|
.putBoolean(KEY_IS_FRIEND_REQUEST, isFriendRequest)
|
||||||
|
.putBoolean(KEY_SHOULD_SEND_SYNC_MESSAGE, shouldSendSyncMessage);
|
||||||
|
|
||||||
if (customFriendRequestMessage != null) { builder.putString(KEY_CUSTOM_FR_MESSAGE, customFriendRequestMessage); }
|
if (customFriendRequestMessage != null) { builder.putString(KEY_CUSTOM_FR_MESSAGE, customFriendRequestMessage); }
|
||||||
return builder.build();
|
return builder.build();
|
||||||
@ -222,7 +228,15 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
|||||||
messageSender.sendMessage(messageId, syncMessage, syncAccess);
|
messageSender.sendMessage(messageId, syncMessage, syncAccess);
|
||||||
return syncAccess.isPresent();
|
return syncAccess.isPresent();
|
||||||
} else {
|
} else {
|
||||||
return messageSender.sendMessage(messageId, address, unidentifiedAccess, textSecureMessage).getSuccess().isUnidentified();
|
LokiSyncMessage syncMessage = null;
|
||||||
|
if (shouldSendSyncMessage) {
|
||||||
|
// Set the sync message destination the primary device, this way it will show that we sent a message to the primary device and not a secondary device
|
||||||
|
String primaryDevice = MultiDeviceUtilitiesKt.getPrimaryDevicePublicKey(address.getNumber());
|
||||||
|
SignalServiceAddress primaryAddress = primaryDevice == null ? address : new SignalServiceAddress(primaryDevice);
|
||||||
|
// We also need to use the original message id and not -1
|
||||||
|
syncMessage = new LokiSyncMessage(primaryAddress, templateMessageId);
|
||||||
|
}
|
||||||
|
return messageSender.sendMessage(messageId, address, unidentifiedAccess, textSecureMessage, Optional.fromNullable(syncMessage)).getSuccess().isUnidentified();
|
||||||
}
|
}
|
||||||
} catch (UnregisteredUserException e) {
|
} catch (UnregisteredUserException e) {
|
||||||
warn(TAG, "Failure", e);
|
warn(TAG, "Failure", e);
|
||||||
@ -241,7 +255,8 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
|||||||
Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION));
|
Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION));
|
||||||
boolean isFriendRequest = data.getBoolean(KEY_IS_FRIEND_REQUEST);
|
boolean isFriendRequest = data.getBoolean(KEY_IS_FRIEND_REQUEST);
|
||||||
String frMessage = data.hasString(KEY_CUSTOM_FR_MESSAGE) ? data.getString(KEY_CUSTOM_FR_MESSAGE) : null;
|
String frMessage = data.hasString(KEY_CUSTOM_FR_MESSAGE) ? data.getString(KEY_CUSTOM_FR_MESSAGE) : null;
|
||||||
return new PushTextSendJob(parameters, templateMessageID, messageID, destination, isFriendRequest, frMessage);
|
boolean shouldSendSyncMessage = data.getBoolean(KEY_SHOULD_SEND_SYNC_MESSAGE);
|
||||||
|
return new PushTextSendJob(parameters, templateMessageID, messageID, destination, isFriendRequest, frMessage, shouldSendSyncMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
src/org/thoughtcrime/securesms/loki/LokiMessageSyncEvent.kt
Normal file
22
src/org/thoughtcrime/securesms/loki/LokiMessageSyncEvent.kt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package org.thoughtcrime.securesms.loki
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.support.v4.content.LocalBroadcastManager
|
||||||
|
|
||||||
|
object LokiMessageSyncEvent {
|
||||||
|
const val MESSAGE_SYNC_EVENT = "com.loki-network.messenger.MESSAGE_SYNC_EVENT"
|
||||||
|
const val MESSAGE_ID = "message_id"
|
||||||
|
const val TIMESTAMP = "timestamp"
|
||||||
|
const val SYNC_MESSAGE = "sync_message"
|
||||||
|
const val TTL = "ttl"
|
||||||
|
|
||||||
|
fun broadcastSecurityUpdateEvent(context: Context, messageID: Long, timestamp: Long, message: ByteArray, ttl: Int) {
|
||||||
|
val intent = Intent(MESSAGE_SYNC_EVENT)
|
||||||
|
intent.putExtra(MESSAGE_ID, messageID)
|
||||||
|
intent.putExtra(TIMESTAMP, timestamp)
|
||||||
|
intent.putExtra(SYNC_MESSAGE, message)
|
||||||
|
intent.putExtra(TTL, ttl)
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
|
||||||
|
}
|
||||||
|
}
|
@ -138,3 +138,68 @@ fun signAndSendPairingAuthorisationMessage(context: Context, pairingAuthorisatio
|
|||||||
Log.w("Loki", "Failed to update device mapping")
|
Log.w("Loki", "Failed to update device mapping")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun shouldSendSycMessage(context: Context, address: Address): Boolean {
|
||||||
|
if (address.isGroup || address.isEmail || address.isMmsGroup) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't send sync messages if it's our address
|
||||||
|
val publicKey = address.serialize()
|
||||||
|
if (publicKey == TextSecurePreferences.getLocalNumber(context)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val storageAPI = LokiStorageAPI.shared
|
||||||
|
val future = SettableFuture<Boolean>()
|
||||||
|
storageAPI.getPrimaryDevicePublicKey(publicKey).success { primaryDevicePublicKey ->
|
||||||
|
val isOurPrimaryDevice = primaryDevicePublicKey != null && TextSecurePreferences.getMasterHexEncodedPublicKey(context) == publicKey
|
||||||
|
// Don't send sync message if the primary device is the same as ours
|
||||||
|
future.set(!isOurPrimaryDevice)
|
||||||
|
}.fail {
|
||||||
|
future.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
future.get()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isOneOfOurDevices(context: Context, address: Address): Boolean {
|
||||||
|
if (address.isGroup || address.isEmail || address.isMmsGroup) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val ourPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
|
val storageAPI = LokiStorageAPI.shared
|
||||||
|
val future = SettableFuture<Boolean>()
|
||||||
|
storageAPI.getAllDevicePublicKeys(ourPublicKey).success {
|
||||||
|
future.set(it.contains(address.serialize()))
|
||||||
|
}.fail {
|
||||||
|
future.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
future.get()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPrimaryDevicePublicKey(hexEncodedPublicKey: String): String? {
|
||||||
|
val storageAPI = LokiStorageAPI.shared
|
||||||
|
val future = SettableFuture<String?>()
|
||||||
|
storageAPI.getPrimaryDevicePublicKey(hexEncodedPublicKey).success {
|
||||||
|
future.set(it)
|
||||||
|
}.fail {
|
||||||
|
future.set(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
future.get()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
package org.thoughtcrime.securesms.loki
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.Address
|
||||||
|
import org.thoughtcrime.securesms.dependencies.InjectableType
|
||||||
|
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.whispersystems.signalservice.api.SignalServiceMessageSender
|
||||||
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class PushMessageSyncSendJob private constructor(
|
||||||
|
parameters: Parameters,
|
||||||
|
private val messageID: Long,
|
||||||
|
private val recipient: Address,
|
||||||
|
private val timestamp: Long,
|
||||||
|
private val message: ByteArray,
|
||||||
|
private val ttl: Int
|
||||||
|
) : BaseJob(parameters), InjectableType {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KEY = "PushMessageSyncSendJob"
|
||||||
|
|
||||||
|
private val TAG = PushMessageSyncSendJob::class.java.simpleName
|
||||||
|
|
||||||
|
private val KEY_MESSAGE_ID = "message_id"
|
||||||
|
private val KEY_RECIPIENT = "recipient"
|
||||||
|
private val KEY_TIMESTAMP = "timestamp"
|
||||||
|
private val KEY_MESSAGE = "message"
|
||||||
|
private val KEY_TTL = "ttl"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var messageSender: SignalServiceMessageSender
|
||||||
|
|
||||||
|
constructor(messageID: Long, recipient: Address, timestamp: Long, message: ByteArray, ttl: Int) : this(Parameters.Builder()
|
||||||
|
.addConstraint(NetworkConstraint.KEY)
|
||||||
|
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||||
|
.setMaxAttempts(3)
|
||||||
|
.build(),
|
||||||
|
messageID, recipient, timestamp, message, ttl)
|
||||||
|
|
||||||
|
override fun serialize(): Data {
|
||||||
|
return Data.Builder()
|
||||||
|
.putLong(KEY_MESSAGE_ID, messageID)
|
||||||
|
.putString(KEY_RECIPIENT, recipient.serialize())
|
||||||
|
.putLong(KEY_TIMESTAMP, timestamp)
|
||||||
|
.putByteArray(KEY_MESSAGE, message)
|
||||||
|
.putInt(KEY_TTL, ttl)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFactoryKey(): String {
|
||||||
|
return KEY
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, UntrustedIdentityException::class)
|
||||||
|
public override fun onRun() {
|
||||||
|
// Don't send sync messages to a group
|
||||||
|
if (recipient.isGroup || recipient.isEmail) { return }
|
||||||
|
messageSender.lokiSendSyncMessage(messageID, SignalServiceAddress(recipient.toPhoneString()), timestamp, message, ttl)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun onShouldRetry(e: Exception): Boolean {
|
||||||
|
// Loki - Disable since we have our own retrying when sending messages
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCanceled() {}
|
||||||
|
|
||||||
|
class Factory : Job.Factory<PushMessageSyncSendJob> {
|
||||||
|
override fun create(parameters: Parameters, data: Data): PushMessageSyncSendJob {
|
||||||
|
try {
|
||||||
|
return PushMessageSyncSendJob(parameters,
|
||||||
|
data.getLong(KEY_MESSAGE_ID),
|
||||||
|
Address.fromSerialized(data.getString(KEY_RECIPIENT)),
|
||||||
|
data.getLong(KEY_TIMESTAMP),
|
||||||
|
data.getByteArray(KEY_MESSAGE),
|
||||||
|
data.getInt(KEY_TTL))
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw AssertionError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,20 +3,17 @@ package org.thoughtcrime.securesms.push;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.SecurityEvent;
|
import org.thoughtcrime.securesms.crypto.SecurityEvent;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
import org.thoughtcrime.securesms.loki.LokiMessageSyncEvent;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
|
||||||
public class SecurityEventListener implements SignalServiceMessageSender.EventListener {
|
public class MessageSenderEventListener implements SignalServiceMessageSender.EventListener {
|
||||||
|
|
||||||
private static final String TAG = SecurityEventListener.class.getSimpleName();
|
private static final String TAG = MessageSenderEventListener.class.getSimpleName();
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
public SecurityEventListener(Context context) {
|
public MessageSenderEventListener(Context context) {
|
||||||
this.context = context.getApplicationContext();
|
this.context = context.getApplicationContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,4 +21,9 @@ public class SecurityEventListener implements SignalServiceMessageSender.EventLi
|
|||||||
public void onSecurityEvent(SignalServiceAddress textSecureAddress) {
|
public void onSecurityEvent(SignalServiceAddress textSecureAddress) {
|
||||||
SecurityEvent.broadcastSecurityUpdateEvent(context);
|
SecurityEvent.broadcastSecurityUpdateEvent(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSyncEvent(long messageID, long timestamp, byte[] message, int ttl) {
|
||||||
|
LokiMessageSyncEvent.INSTANCE.broadcastSecurityUpdateEvent(context, messageID, timestamp, message, ttl);
|
||||||
|
}
|
||||||
}
|
}
|
@ -44,6 +44,7 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
|||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.loki.GeneralUtilitiesKt;
|
import org.thoughtcrime.securesms.loki.GeneralUtilitiesKt;
|
||||||
import org.thoughtcrime.securesms.loki.MultiDeviceUtilitiesKt;
|
import org.thoughtcrime.securesms.loki.MultiDeviceUtilitiesKt;
|
||||||
|
import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob;
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||||
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
||||||
@ -149,6 +150,28 @@ public class MessageSender {
|
|||||||
return allocatedThreadId;
|
return allocatedThreadId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void sendSyncMessageToOurDevices(final Context context,
|
||||||
|
final long messageID,
|
||||||
|
final long timestamp,
|
||||||
|
final byte[] message,
|
||||||
|
final int ttl) {
|
||||||
|
String ourPublicKey = TextSecurePreferences.getLocalNumber(context);
|
||||||
|
LokiStorageAPI storageAPI = LokiStorageAPI.Companion.getShared();
|
||||||
|
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
||||||
|
|
||||||
|
storageAPI.getAllDevicePublicKeys(ourPublicKey).success(devices -> {
|
||||||
|
for (String device : devices) {
|
||||||
|
// Don't send to ourselves
|
||||||
|
if (device.equals(ourPublicKey)) { continue; }
|
||||||
|
|
||||||
|
// Create a send job for our device
|
||||||
|
Address address = Address.fromSerialized(device);
|
||||||
|
jobManager.add(new PushMessageSyncSendJob(messageID, address, timestamp, message, ttl));
|
||||||
|
}
|
||||||
|
return Unit.INSTANCE;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static void resendGroupMessage(Context context, MessageRecord messageRecord, Address filterAddress) {
|
public static void resendGroupMessage(Context context, MessageRecord messageRecord, Address filterAddress) {
|
||||||
if (!messageRecord.isMms()) throw new AssertionError("Not Group");
|
if (!messageRecord.isMms()) throw new AssertionError("Not Group");
|
||||||
sendGroupPush(context, messageRecord.getRecipient(), messageRecord.getId(), filterAddress);
|
sendGroupPush(context, messageRecord.getRecipient(), messageRecord.getId(), filterAddress);
|
||||||
@ -204,22 +227,27 @@ public class MessageSender {
|
|||||||
jobManager.add(new PushTextSendJob(messageId, recipient.getAddress()));
|
jobManager.add(new PushTextSendJob(messageId, recipient.getAddress()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
boolean[] hasSentSyncMessage = { false };
|
||||||
|
|
||||||
MultiDeviceUtilitiesKt.getAllDevicePublicKeys(context, recipientPublicKey, storageAPI, (devicePublicKey, isFriend, friendCount) -> {
|
MultiDeviceUtilitiesKt.getAllDevicePublicKeys(context, recipientPublicKey, storageAPI, (devicePublicKey, isFriend, friendCount) -> {
|
||||||
|
Util.runOnMain(() -> {
|
||||||
Address address = Address.fromSerialized(devicePublicKey);
|
Address address = Address.fromSerialized(devicePublicKey);
|
||||||
long messageIDToUse = recipientPublicKey.equals(devicePublicKey) ? messageId : -1L;
|
long messageIDToUse = recipientPublicKey.equals(devicePublicKey) ? messageId : -1L;
|
||||||
|
|
||||||
if (isFriend) {
|
if (isFriend) {
|
||||||
// Send a normal message if the user is friends with the recipient
|
// Send a normal message if the user is friends with the recipient
|
||||||
jobManager.add(new PushTextSendJob(messageId, messageIDToUse, address));
|
// We should also send a sync message if we haven't already sent one
|
||||||
|
boolean shouldSendSyncMessage = !hasSentSyncMessage[0] && MultiDeviceUtilitiesKt.shouldSendSycMessage(context, address);
|
||||||
|
jobManager.add(new PushTextSendJob(messageId, messageIDToUse, address, shouldSendSyncMessage));
|
||||||
|
hasSentSyncMessage[0] = shouldSendSyncMessage;
|
||||||
} else {
|
} else {
|
||||||
// Send friend requests to non friends. If the user is friends with any
|
// Send friend requests to non friends. If the user is friends with any
|
||||||
// of the devices then send out a default friend request message.
|
// of the devices then send out a default friend request message.
|
||||||
boolean isFriendsWithAny = (friendCount > 0);
|
boolean isFriendsWithAny = (friendCount > 0);
|
||||||
String defaultFriendRequestMessage = isFriendsWithAny ? "Accept this friend request to enable messages to be synced across devices" : null;
|
String defaultFriendRequestMessage = isFriendsWithAny ? "Accept this friend request to enable messages to be synced across devices" : null;
|
||||||
jobManager.add(new PushTextSendJob(messageId, messageIDToUse, address, true, defaultFriendRequestMessage));
|
jobManager.add(new PushTextSendJob(messageId, messageIDToUse, address, true, defaultFriendRequestMessage, false));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -231,25 +259,31 @@ public class MessageSender {
|
|||||||
// Just send the message normally if it's a group message
|
// Just send the message normally if it's a group message
|
||||||
String recipientPublicKey = recipient.getAddress().serialize();
|
String recipientPublicKey = recipient.getAddress().serialize();
|
||||||
if (GeneralUtilitiesKt.isPublicChat(context, recipientPublicKey)) {
|
if (GeneralUtilitiesKt.isPublicChat(context, recipientPublicKey)) {
|
||||||
PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress());
|
PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress(), false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean[] hasSentSyncMessage = { false };
|
||||||
|
|
||||||
MultiDeviceUtilitiesKt.getAllDevicePublicKeys(context, recipientPublicKey, storageAPI, (devicePublicKey, isFriend, friendCount) -> {
|
MultiDeviceUtilitiesKt.getAllDevicePublicKeys(context, recipientPublicKey, storageAPI, (devicePublicKey, isFriend, friendCount) -> {
|
||||||
|
Util.runOnMain(() -> {
|
||||||
Address address = Address.fromSerialized(devicePublicKey);
|
Address address = Address.fromSerialized(devicePublicKey);
|
||||||
long messageIDToUse = recipientPublicKey.equals(devicePublicKey) ? messageId : -1L;
|
long messageIDToUse = recipientPublicKey.equals(devicePublicKey) ? messageId : -1L;
|
||||||
|
|
||||||
if (isFriend) {
|
if (isFriend) {
|
||||||
// Send a normal message if the user is friends with the recipient
|
// Send a normal message if the user is friends with the recipient
|
||||||
PushMediaSendJob.enqueue(context, jobManager, messageId, messageIDToUse, address);
|
// We should also send a sync message if we haven't already sent one
|
||||||
|
boolean shouldSendSyncMessage = !hasSentSyncMessage[0] && MultiDeviceUtilitiesKt.shouldSendSycMessage(context, address);
|
||||||
|
PushMediaSendJob.enqueue(context, jobManager, messageId, messageIDToUse, address, shouldSendSyncMessage);
|
||||||
|
hasSentSyncMessage[0] = shouldSendSyncMessage;
|
||||||
} else {
|
} else {
|
||||||
// Send friend requests to non friends. If the user is friends with any
|
// Send friend requests to non friends. If the user is friends with any
|
||||||
// of the devices then send out a default friend request message.
|
// of the devices then send out a default friend request message.
|
||||||
boolean isFriendsWithAny = friendCount > 0;
|
boolean isFriendsWithAny = friendCount > 0;
|
||||||
String defaultFriendRequestMessage = isFriendsWithAny ? "Accept this friend request to enable messages to be synced across devices" : null;
|
String defaultFriendRequestMessage = isFriendsWithAny ? "Accept this friend request to enable messages to be synced across devices" : null;
|
||||||
PushMediaSendJob.enqueue(context, jobManager, messageId, messageIDToUse, address, true, defaultFriendRequestMessage);
|
PushMediaSendJob.enqueue(context, jobManager, messageId, messageIDToUse, address, true, defaultFriendRequestMessage, false);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user