Use promises instead of blocking the thread.

This commit is contained in:
Mikunj 2019-11-01 12:15:40 +11:00
parent bdc0ed36eb
commit 789aa244b5
9 changed files with 182 additions and 127 deletions

View File

@ -91,7 +91,7 @@ public class TypingStatusSender {
ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(threadId, typingStarted));
return;
}
LokiStorageAPI.shared.getAllDevicePublicKeysAsync(recipient.getAddress().serialize()).success(devices -> {
LokiStorageAPI.shared.getAllDevicePublicKeys(recipient.getAddress().serialize()).success(devices -> {
for (String device : devices) {
Recipient deviceRecipient = Recipient.from(context, Address.fromSerialized(device), false);
long deviceThreadID = threadDatabase.getThreadIdIfExistsFor(deviceRecipient);

View File

@ -127,6 +127,7 @@ import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.Database;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
@ -2192,7 +2193,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (threadID != this.threadId) {
Recipient threadRecipient = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID);
if (threadRecipient != null && !threadRecipient.isGroupRecipient()) {
LokiStorageAPI.shared.getAllDevicePublicKeysAsync(threadRecipient.getAddress().serialize()).success(devices -> {
LokiStorageAPI.shared.getAllDevicePublicKeys(threadRecipient.getAddress().serialize()).success(devices -> {
// We should update our input if this thread is a part of the other threads device
if (devices.contains(recipient.getAddress().serialize())) {
this.updateInputPanel();
@ -2212,10 +2213,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
return;
}
task(() -> {
// Run the functions below in a background thread since they are blocking
return MultiDeviceUtilities.isFriendsWithAnyLinkedDevice(this, recipient) || !hasPendingFriendRequestWithAnyLinkedDevice();
}).success(shouldEnableInput -> {
// It could take a while before our promise resolves, so we assume the best case
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(this).getFriendRequestStatus(threadId);
boolean isPending = friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENDING || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED;
setInputPanelEnabled(!isPending);
// This promise correctly updates the UI for multidevice
MultiDeviceUtilities.shouldEnableUserInput(this, recipient).success(shouldEnableInput -> {
setInputPanelEnabled(shouldEnableInput);
return Unit.INSTANCE;
});
@ -3054,20 +3058,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
updateInputPanel();
}
public boolean hasPendingFriendRequestWithAnyLinkedDevice() {
if (recipient.isGroupRecipient()) return false;
// This call will block the thread that is being run on! be careful
Map<String, LokiThreadFriendRequestStatus> map = MultiDeviceUtilities.getAllDeviceFriendRequestStatuses(this, recipient.getAddress().serialize());
for (LokiThreadFriendRequestStatus status : map.values()) {
if (status == LokiThreadFriendRequestStatus.REQUEST_SENDING || status == LokiThreadFriendRequestStatus.REQUEST_SENT || status == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
return true;
}
}
return false;
}
public boolean isNoteToSelf() {
return TextSecurePreferences.getLocalNumber(this).equalsIgnoreCase(recipient.getAddress().serialize());
}

View File

@ -134,6 +134,7 @@ import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestS
import org.whispersystems.signalservice.loki.messaging.LokiServiceMessage;
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
import org.whispersystems.signalservice.loki.messaging.LokiThreadSessionResetStatus;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
import java.security.MessageDigest;
import java.security.SecureRandom;
@ -1094,15 +1095,19 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
// it must be a friend request accepted message. Declining a friend request doesn't send a message.
lokiThreadDatabase.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS);
// Update the last message if needed
String primaryDevice = LokiStorageAPI.shared.getPrimaryDevicePublicKey(pubKey);
long primaryDeviceThreadID = primaryDevice == null ? threadID : DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(primaryDevice), false));
FriendRequestHandler.updateLastFriendRequestMessage(context, primaryDeviceThreadID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED);
LokiStorageAPI.shared.getPrimaryDevicePublicKey(pubKey).success(primaryDevice -> {
Util.runOnMain(() -> {
long primaryDeviceThreadID = primaryDevice == null ? threadID : DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(primaryDevice), false));
FriendRequestHandler.updateLastFriendRequestMessage(context, primaryDeviceThreadID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED);
});
return Unit.INSTANCE;
});
}
private void updateFriendRequestStatusIfNeeded(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) {
if (!envelope.isFriendRequest() || isGroupChatMessage(message)) { return; }
// This handles the case where another user sends us a regular message without authorisation
boolean shouldBecomeFriends = MultiDeviceUtilities.shouldAutomaticallyBecomeFriendsWithDevice(content.getSender(), context);
boolean shouldBecomeFriends = PromiseUtil.get(MultiDeviceUtilities.shouldAutomaticallyBecomeFriendsWithDevice(content.getSender(), context), false);
if (shouldBecomeFriends) {
// Become friends AND update the message they sent
becomeFriendsWithContact(content.getSender());
@ -1113,9 +1118,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
Recipient originalRecipient = getMessageDestination(content, message);
Recipient primaryDeviceRecipient = getMessagePrimaryDestination(content, message);
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
SmsDatabase smsMessageDatabase = DatabaseFactory.getSmsDatabase(context);
MmsDatabase mmsMessageDatabase = DatabaseFactory.getMmsDatabase(context);
LokiMessageDatabase lokiMessageDatabase= DatabaseFactory.getLokiMessageDatabase(context);
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(originalRecipient);
long primaryDeviceThreadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(primaryDeviceRecipient);
@ -1532,7 +1534,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
private Recipient getPrimaryDeviceRecipient(String recipient) {
try {
String primaryDevice = LokiStorageAPI.shared.getPrimaryDevicePublicKey(recipient);
String primaryDevice = LokiStorageAPI.shared.getPrimaryDevicePublicKey(recipient).get();
String publicKey = (primaryDevice != null) ? primaryDevice : recipient;
// If the public key matches our primary device then we need to forward the message to ourselves (Note to self)
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
@ -1593,10 +1595,16 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} else if (content.getCallMessage().isPresent() || content.getTypingMessage().isPresent()) {
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 = MultiDeviceUtilities.isOneOfOurDevices(context, sender.getAddress());
if (!isOurDevice) { Log.w(TAG, "Got a sync message from a device that is not ours!."); }
return !isOurDevice;
try {
// We should ignore a sync message if the sender is not one of our devices
boolean isOurDevice = MultiDeviceUtilities.isOneOfOurDevices(context, sender.getAddress()).get();
if (!isOurDevice) {
Log.w(TAG, "Got a sync message from a device that is not ours!.");
}
return !isOurDevice;
} catch (Exception e) {
return true;
}
}
return false;

View File

@ -45,6 +45,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -281,7 +282,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
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 = LokiStorageAPI.shared.getPrimaryDevicePublicKey(address.getNumber());
String primaryDevice = PromiseUtil.get(LokiStorageAPI.shared.getPrimaryDevicePublicKey(address.getNumber()), null);
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);

View File

@ -32,6 +32,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
import java.io.IOException;
@ -232,7 +233,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
LokiSyncMessage syncMessage = null;
if (shouldSendSyncMessage) {
// Set the sync message destination to the primary device, this way it will show that we sent a message to the primary device and not a secondary device
String primaryDevice = LokiStorageAPI.shared.getPrimaryDevicePublicKey(address.getNumber());
String primaryDevice = PromiseUtil.get(LokiStorageAPI.shared.getPrimaryDevicePublicKey(address.getNumber()), null);
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);

View File

@ -20,6 +20,7 @@ import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage.Action;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
import java.util.Collections;
import java.util.List;
@ -98,7 +99,10 @@ public class TypingSendJob extends BaseJob implements InjectableType {
SignalServiceTypingMessage typingMessage = new SignalServiceTypingMessage(typing ? Action.STARTED : Action.STOPPED, System.currentTimeMillis(), groupId);
// Loki - Don't send typing indicators in group chats or to ourselves
if (!recipient.isGroupRecipient() && !MultiDeviceUtilities.isOneOfOurDevices(context, recipient.getAddress())) {
if (recipient.isGroupRecipient()) { return; }
boolean isOurDevice = PromiseUtil.get(MultiDeviceUtilities.isOneOfOurDevices(context, recipient.getAddress()), false);
if (!isOurDevice) {
messageSender.sendTyping(0, addresses, unidentifiedAccess, typingMessage);
}
}

View File

@ -5,6 +5,7 @@ import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.ui.alwaysUi
import nl.komponents.kovenant.ui.successUi
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.loki.api.LokiStorageAPI
import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
@ -34,8 +35,10 @@ object FriendRequestHandler {
if (type == ActionType.Sending) {
// We only want to update message status if we aren't friends with another of their devices
// This avoids spam in the ui where it would keep telling the user that they sent a friend request on every single message
if (!isFriendsWithAnyLinkedDevice(context, recipient) && friendRequestStatus == LokiMessageFriendRequestStatus.NONE) {
messageDatabase.setFriendRequestStatus(messageId, LokiMessageFriendRequestStatus.REQUEST_SENDING)
isFriendsWithAnyLinkedDevice(context, recipient).successUi { isFriends ->
if (!isFriends && friendRequestStatus == LokiMessageFriendRequestStatus.NONE) {
messageDatabase.setFriendRequestStatus(messageId, LokiMessageFriendRequestStatus.REQUEST_SENDING)
}
}
} else if (friendRequestStatus != LokiMessageFriendRequestStatus.NONE) {
// Update the friend request status of the message if we have it
@ -70,23 +73,25 @@ object FriendRequestHandler {
// We only want to update the last message status if we're not friends with any of their linked devices
// This ensures that we don't spam the UI with accept/decline messages
val recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId) ?: return
if (isFriendsWithAnyLinkedDevice(context, recipient)) { return }
isFriendsWithAnyLinkedDevice(context, recipient).successUi { isFriends ->
if (isFriends) { return@successUi }
// Since messages are forwarded to the primary device thread, we need to update it there
val messageCount = smsMessageDatabase.getMessageCountForThread(threadId)
val messageID = smsMessageDatabase.getIDForMessageAtIndex(threadId, messageCount - 1) // The message that was just received
if (messageID < 0) { return }
// Since messages are forwarded to the primary device thread, we need to update it there
val messageCount = smsMessageDatabase.getMessageCountForThread(threadId)
val messageID = smsMessageDatabase.getIDForMessageAtIndex(threadId, messageCount - 1) // The message that was just received
if (messageID < 0) { return@successUi }
val messageDatabase = DatabaseFactory.getLokiMessageDatabase(context)
val messageDatabase = DatabaseFactory.getLokiMessageDatabase(context)
// We need to go through and set all messages which are REQUEST_PENDING to NONE
smsMessageDatabase.getAllMessageIDs(threadId)
.filter { messageDatabase.getFriendRequestStatus(it) == LokiMessageFriendRequestStatus.REQUEST_PENDING }
.forEach {
messageDatabase.setFriendRequestStatus(it, LokiMessageFriendRequestStatus.NONE)
}
// We need to go through and set all messages which are REQUEST_PENDING to NONE
smsMessageDatabase.getAllMessageIDs(threadId)
.filter { messageDatabase.getFriendRequestStatus(it) == LokiMessageFriendRequestStatus.REQUEST_PENDING }
.forEach {
messageDatabase.setFriendRequestStatus(it, LokiMessageFriendRequestStatus.NONE)
}
// Set the last message to pending
messageDatabase.setFriendRequestStatus(messageID, LokiMessageFriendRequestStatus.REQUEST_PENDING)
// Set the last message to pending
messageDatabase.setFriendRequestStatus(messageID, LokiMessageFriendRequestStatus.REQUEST_PENDING)
}
}
}

View File

@ -3,9 +3,9 @@ package org.thoughtcrime.securesms.loki
import android.content.Context
import android.os.Handler
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.*
import nl.komponents.kovenant.functional.bind
import nl.komponents.kovenant.functional.map
import nl.komponents.kovenant.toFailVoid
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.database.Address
@ -20,28 +20,26 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.loki.api.LokiStorageAPI
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
import org.whispersystems.signalservice.loki.utilities.recover
import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
/*
All functions within this class, excluding the ones which return promises, BLOCK the thread! Don't run them on the main thread!
*/
fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: String): Map<String, LokiThreadFriendRequestStatus> {
fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: String): Promise<Map<String, LokiThreadFriendRequestStatus>, Exception> {
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
val keys = LokiStorageAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey)
val map = mutableMapOf<String, LokiThreadFriendRequestStatus>()
for (devicePublicKey in keys) {
val device = Recipient.from(context, Address.fromSerialized(devicePublicKey), false)
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(device)
val friendRequestStatus = if (threadID < 0) LokiThreadFriendRequestStatus.NONE else lokiThreadDatabase.getFriendRequestStatus(threadID)
map[devicePublicKey] = friendRequestStatus
}
return map
return LokiStorageAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey).map { keys ->
val map = mutableMapOf<String, LokiThreadFriendRequestStatus>()
for (devicePublicKey in keys) {
val device = Recipient.from(context, Address.fromSerialized(devicePublicKey), false)
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(device)
val friendRequestStatus = if (threadID < 0) LokiThreadFriendRequestStatus.NONE else lokiThreadDatabase.getFriendRequestStatus(threadID)
map[devicePublicKey] = friendRequestStatus
}
map
}.recover { mutableMapOf() }
}
fun getAllDevicePublicKeysWithFriendStatus(context: Context, hexEncodedPublicKey: String): Promise<Map<String, Boolean>, Unit> {
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
return LokiStorageAPI.shared.getAllDevicePublicKeysAsync(hexEncodedPublicKey).map { keys ->
return LokiStorageAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey).map { keys ->
val devices = keys.toMutableSet()
if (hexEncodedPublicKey != userHexEncodedPublicKey) {
devices.remove(userHexEncodedPublicKey)
@ -59,22 +57,31 @@ fun getFriendCount(context: Context, devices: Set<String>): Int {
return getFriendPublicKeys(context, devices).count()
}
fun shouldAutomaticallyBecomeFriendsWithDevice(publicKey: String, context: Context): Boolean {
fun shouldAutomaticallyBecomeFriendsWithDevice(publicKey: String, context: Context): Promise<Boolean, Exception> {
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
val storageAPI = LokiStorageAPI.shared
// If the public key doesn't have any other devices then go through regular friend request logic
val primaryDevicePublicKey = storageAPI.getPrimaryDevicePublicKey(publicKey) ?: return false
// If this is one of our devices then we should become friends
if (isOneOfOurDevices(context, publicKey)) {
return true
// If this public key is our primary device then we should become friends
if (publicKey == TextSecurePreferences.getMasterHexEncodedPublicKey(context)) {
return Promise.of(true)
}
// If we are friends with the primary device then we should become friends
val primaryDevice = Recipient.from(context, Address.fromSerialized(primaryDevicePublicKey), false)
val primaryDeviceThreadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(primaryDevice)
return primaryDeviceThreadID >= 0 && lokiThreadDatabase.getFriendRequestStatus(primaryDeviceThreadID) == LokiThreadFriendRequestStatus.FRIENDS
return storageAPI.getPrimaryDevicePublicKey(publicKey).map { primaryDevicePublicKey ->
// If the public key doesn't have any other devices then go through regular friend request logic
if (primaryDevicePublicKey == null) {
return@map false
}
// If the primary device public key matches our primary device then we should become friends since this is our other device
if (primaryDevicePublicKey == TextSecurePreferences.getMasterHexEncodedPublicKey(context)) {
return@map true
}
// If we are friends with the primary device then we should become friends
val primaryDevice = Recipient.from(context, Address.fromSerialized(primaryDevicePublicKey), false)
val primaryDeviceThreadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(primaryDevice)
primaryDeviceThreadID >= 0 && lokiThreadDatabase.getFriendRequestStatus(primaryDeviceThreadID) == LokiThreadFriendRequestStatus.FRIENDS
}
}
fun sendPairingAuthorisationMessage(context: Context, contactHexEncodedPublicKey: String, authorisation: PairingAuthorisation): Promise<Unit, Exception> {
@ -125,37 +132,55 @@ fun signAndSendPairingAuthorisationMessage(context: Context, pairingAuthorisatio
}
fun shouldSendSycMessage(context: Context, address: Address): Boolean {
fun shouldSendSycMessage(context: Context, address: Address): Promise<Boolean, Exception> {
if (address.isGroup || address.isEmail || address.isMmsGroup) {
return false
return Promise.of(false)
}
// Don't send sync messages if it's one of our devices
return !isOneOfOurDevices(context, address)
return isOneOfOurDevices(context, address).map { !it }
}
fun isOneOfOurDevices(context: Context, publicKey: String): Boolean {
return isOneOfOurDevices(context, Address.fromSerialized(publicKey))
}
fun isOneOfOurDevices(context: Context, address: Address): Boolean {
fun isOneOfOurDevices(context: Context, address: Address): Promise<Boolean, Exception> {
if (address.isGroup || address.isEmail || address.isMmsGroup) {
return false
return Promise.of(false)
}
val ourPublicKey = TextSecurePreferences.getLocalNumber(context)
val devices = LokiStorageAPI.shared.getAllDevicePublicKeys(ourPublicKey)
return devices.contains(address.serialize())
return LokiStorageAPI.shared.getAllDevicePublicKeys(ourPublicKey).map { devices ->
devices.contains(address.serialize())
}
}
fun isFriendsWithAnyLinkedDevice(context: Context, recipient: Recipient): Boolean {
if (recipient.isGroupRecipient) return true
fun isFriendsWithAnyLinkedDevice(context: Context, recipient: Recipient): Promise<Boolean, Exception> {
if (recipient.isGroupRecipient) { return Promise.of(true) }
val map = getAllDeviceFriendRequestStatuses(context, recipient.address.serialize())
for (status in map.values) {
if (status == LokiThreadFriendRequestStatus.FRIENDS) {
return true
return getAllDeviceFriendRequestStatuses(context, recipient.address.serialize()).map { map ->
for (status in map.values) {
if (status == LokiThreadFriendRequestStatus.FRIENDS) {
return@map true
}
}
false
}
return false
}
}
fun hasPendingFriendRequestWithAnyLinkedDevice(context: Context, recipient: Recipient): Promise<Boolean, Exception> {
if (recipient.isGroupRecipient) { return Promise.of(false) }
return getAllDeviceFriendRequestStatuses(context, recipient.address.serialize()).map { map ->
for (status in map.values) {
if (status == LokiThreadFriendRequestStatus.REQUEST_SENDING || status == LokiThreadFriendRequestStatus.REQUEST_SENT || status == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
return@map true
}
}
false
}
}
fun shouldEnableUserInput(context: Context, recipient: Recipient): Promise<Boolean, Exception> {
// Input should be enabled if we don't have any pending requests OR we're friends with any linked device
return hasPendingFriendRequestWithAnyLinkedDevice(context, recipient).bind { hasPendingFriendRequest ->
if (!hasPendingFriendRequest) Promise.of(true) else isFriendsWithAnyLinkedDevice(context, recipient)
}.recover { true }
}

View File

@ -61,12 +61,15 @@ import org.whispersystems.signalservice.api.push.ContactTokenDetails;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import kotlin.Unit;
import nl.komponents.kovenant.Kovenant;
import nl.komponents.kovenant.Promise;
public class MessageSender {
@ -79,20 +82,24 @@ public class MessageSender {
sendBackgroundMessage(context, contactHexEncodedPublicKey);
// Go through the other devices and only send background messages if we're friends or we have received friend request
Set<String> devices = LokiStorageAPI.shared.getAllDevicePublicKeys(contactHexEncodedPublicKey);
for (String device : devices) {
// Don't send message to the device we already have sent to
if (device.equals(contactHexEncodedPublicKey)) { continue; }
Recipient recipient = Recipient.from(context, Address.fromSerialized(device), false);
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient);
if (threadID < 0) { continue; }
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID);
if (friendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
sendBackgroundMessage(context, device);
}
LokiStorageAPI.shared.getAllDevicePublicKeys(contactHexEncodedPublicKey).success(devices -> {
Util.runOnMain(() -> {
for (String device : devices) {
// Don't send message to the device we already have sent to
if (device.equals(contactHexEncodedPublicKey)) { continue; }
Recipient recipient = Recipient.from(context, Address.fromSerialized(device), false);
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient);
if (threadID < 0) { continue; }
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID);
if (friendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
sendBackgroundMessage(context, device);
}
// TODO: Do we want to send a custom FR Message if we're not friends and we haven't received a friend request?
}
// TODO: Do we want to send a custom FR Message if we're not friends and we haven't received a friend request?
}
});
return Unit.INSTANCE;
});
}
public static void sendBackgroundMessage(Context context, String contactHexEncodedPublicKey) {
@ -199,15 +206,19 @@ public class MessageSender {
final int ttl) {
String ourPublicKey = TextSecurePreferences.getLocalNumber(context);
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
Set<String> devices = LokiStorageAPI.shared.getAllDevicePublicKeys(ourPublicKey);
for (String device : devices) {
// Don't send to ourselves
if (device.equals(ourPublicKey)) { continue; }
LokiStorageAPI.shared.getAllDevicePublicKeys(ourPublicKey).success(devices -> {
Util.runOnMain(() -> {
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));
}
// 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) {
@ -267,9 +278,8 @@ public class MessageSender {
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
// Just send the message normally if it's a group message or we're sending to one of our devices
// TODO: Test how badly this will block the thread
String recipientPublicKey = recipient.getAddress().serialize();
if (GeneralUtilitiesKt.isPublicChat(context, recipientPublicKey) || MultiDeviceUtilities.isOneOfOurDevices(context, recipient.getAddress())) {
if (GeneralUtilitiesKt.isPublicChat(context, recipientPublicKey) || PromiseUtil.get(MultiDeviceUtilities.isOneOfOurDevices(context, recipient.getAddress()), false)) {
if (type == MessageType.MEDIA) {
PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress(), false);
} else {
@ -292,13 +302,24 @@ public class MessageSender {
if (isFriend) {
// Send a normal message if the user is friends with the recipient
// We should also send a sync message if we haven't already sent one
boolean shouldSendSyncMessage = !hasSentSyncMessage[0] && MultiDeviceUtilities.shouldSendSycMessage(context, address);
if (type == MessageType.MEDIA) {
PushMediaSendJob.enqueue(context, jobManager, messageId, messageIDToUse, address, shouldSendSyncMessage);
} else {
jobManager.add(new PushTextSendJob(messageId, messageIDToUse, address, shouldSendSyncMessage));
Promise<Boolean, Exception> promise = Promise.Companion.of(false, Kovenant.INSTANCE.getContext());
if (!hasSentSyncMessage[0]) {
promise = MultiDeviceUtilities.shouldSendSycMessage(context, address).success(value -> {
hasSentSyncMessage[0] = value;
return Unit.INSTANCE;
});
}
hasSentSyncMessage[0] = shouldSendSyncMessage;
promise.success(shouldSendSyncMessage -> {
Util.runOnMain(() -> {
if (type == MessageType.MEDIA) {
PushMediaSendJob.enqueue(context, jobManager, messageId, messageIDToUse, address, shouldSendSyncMessage);
} else {
jobManager.add(new PushTextSendJob(messageId, messageIDToUse, address, shouldSendSyncMessage));
}
});
return Unit.INSTANCE;
});
} else {
// Send friend requests to non friends. If the user is friends with any
// of the devices then send out a default friend request message.