Stop main thread from being blocked.

Refactoring.
This commit is contained in:
Mikunj 2019-10-31 11:36:52 +11:00
parent 4072f506b5
commit 31ad7a40de
8 changed files with 138 additions and 136 deletions

View File

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

View File

@ -249,7 +249,11 @@ import java.util.concurrent.atomic.AtomicInteger;
import kotlin.Unit; import kotlin.Unit;
import network.loki.messenger.R; import network.loki.messenger.R;
import nl.komponents.kovenant.Kovenant;
import nl.komponents.kovenant.KovenantApi;
import nl.komponents.kovenant.Promise;
import static nl.komponents.kovenant.KovenantApi.task;
import static org.thoughtcrime.securesms.TransportOption.Type; import static org.thoughtcrime.securesms.TransportOption.Type;
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK; import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
@ -2185,44 +2189,50 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void handleThreadFriendRequestStatusChanged(long threadID) { public void handleThreadFriendRequestStatusChanged(long threadID) {
Util.runOnMain(() -> {
boolean shouldUpdateInputPanel = true;
if (threadID != this.threadId) { if (threadID != this.threadId) {
Recipient threadRecipient = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID); Recipient threadRecipient = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID);
if (threadRecipient != null && !threadRecipient.isGroupRecipient()) { if (threadRecipient != null && !threadRecipient.isGroupRecipient()) {
LokiStorageAPI.shared.getAllDevicePublicKeysAsync(threadRecipient.getAddress().serialize()).success(devices -> {
// We should update our input if this thread is a part of the other threads device // We should update our input if this thread is a part of the other threads device
Set<String> devices = LokiStorageAPI.shared.getAllDevicePublicKeys(threadRecipient.getAddress().serialize()); if (devices.contains(recipient.getAddress().serialize())) {
shouldUpdateInputPanel = devices.contains(recipient.getAddress().serialize());
} else {
shouldUpdateInputPanel = false;
}
}
if (shouldUpdateInputPanel) {
this.updateInputPanel(); this.updateInputPanel();
} }
return Unit.INSTANCE;
}); });
} }
return;
}
this.updateInputPanel();
}
private void updateInputPanel() { private void updateInputPanel() {
if (recipient.isGroupRecipient()) { if (recipient.isGroupRecipient() || isNoteToSelf()) {
updateToggleButtonState(); setInputPanelEnabled(true);
inputPanel.setEnabled(true); return;
inputPanel.composeText.requestFocus();
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(inputPanel.composeText, 0);
} }
boolean hasPendingFriendRequest = !recipient.isGroupRecipient() && hasPendingFriendRequestWithAnyLinkedDevice();
boolean isFriendsWithAnyLinkedDevices = MultiDeviceUtilities.isFriendsWithAnyLinkedDevice(this, recipient); task(() -> {
boolean shouldEnableInput = isFriendsWithAnyLinkedDevices || !hasPendingFriendRequest; // Run the functions below in a background thread since they are blocking
return MultiDeviceUtilities.isFriendsWithAnyLinkedDevice(this, recipient) || !hasPendingFriendRequestWithAnyLinkedDevice();
}).success(shouldEnableInput -> {
setInputPanelEnabled(shouldEnableInput);
return Unit.INSTANCE;
});
}
private void setInputPanelEnabled(boolean enabled) {
Util.runOnMain(() -> {
updateToggleButtonState(); updateToggleButtonState();
inputPanel.setEnabled(shouldEnableInput); int hintID = enabled ? R.string.activity_conversation_default_hint : R.string.activity_conversation_pending_friend_request_hint;
int hintID = shouldEnableInput ? R.string.activity_conversation_default_hint : R.string.activity_conversation_pending_friend_request_hint;
inputPanel.setHint(getResources().getString(hintID)); inputPanel.setHint(getResources().getString(hintID));
if (shouldEnableInput) { inputPanel.setEnabled(enabled);
if (enabled) {
inputPanel.composeText.requestFocus(); inputPanel.composeText.requestFocus();
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(inputPanel.composeText, 0); inputMethodManager.showSoftInput(inputPanel.composeText, 0);
} }
});
} }
private void sendMessage() { private void sendMessage() {
@ -2428,7 +2438,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void updateToggleButtonState() { private void updateToggleButtonState() {
// Don't allow attachments if we're not friends // Don't allow attachments if we're not friends
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(this).getFriendRequestStatus(threadId); LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(this).getFriendRequestStatus(threadId);
if (!recipient.isGroupRecipient() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) { if (!isNoteToSelf() && !recipient.isGroupRecipient() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) {
buttonToggle.display(sendButton); buttonToggle.display(sendButton);
quickAttachmentToggle.hide(); quickAttachmentToggle.hide();
inlineAttachmentToggle.hide(); inlineAttachmentToggle.hide();
@ -3047,6 +3057,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public boolean hasPendingFriendRequestWithAnyLinkedDevice() { public boolean hasPendingFriendRequestWithAnyLinkedDevice() {
if (recipient.isGroupRecipient()) return false; 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()); Map<String, LokiThreadFriendRequestStatus> map = MultiDeviceUtilities.getAllDeviceFriendRequestStatuses(this, recipient.getAddress().serialize());
for (LokiThreadFriendRequestStatus status : map.values()) { for (LokiThreadFriendRequestStatus status : map.values()) {
if (status == LokiThreadFriendRequestStatus.REQUEST_SENDING || status == LokiThreadFriendRequestStatus.REQUEST_SENT || status == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) { if (status == LokiThreadFriendRequestStatus.REQUEST_SENDING || status == LokiThreadFriendRequestStatus.REQUEST_SENT || status == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
@ -3056,5 +3067,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
return false; return false;
} }
public boolean isNoteToSelf() {
return TextSecurePreferences.getLocalNumber(this).equalsIgnoreCase(recipient.getAddress().serialize());
}
// endregion // endregion
} }

View File

@ -4,6 +4,8 @@ package org.thoughtcrime.securesms.loki
import android.content.Context import android.content.Context
import android.os.Handler import android.os.Handler
import nl.komponents.kovenant.Promise import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.functional.map
import nl.komponents.kovenant.toFailVoid
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.database.Address import org.thoughtcrime.securesms.database.Address
@ -20,6 +22,10 @@ import org.whispersystems.signalservice.loki.api.PairingAuthorisation
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
import org.whispersystems.signalservice.loki.utilities.retryIfNeeded 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): Map<String, LokiThreadFriendRequestStatus> {
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context) val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
val keys = LokiStorageAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey) val keys = LokiStorageAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey)
@ -33,18 +39,20 @@ fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: Str
return map return map
} }
fun getAllDevicePublicKeysWithFriendStatus(context: Context, hexEncodedPublicKey: String): Map<String, Boolean> { fun getAllDevicePublicKeysWithFriendStatus(context: Context, hexEncodedPublicKey: String): Promise<Map<String, Boolean>, Unit> {
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
val devices = LokiStorageAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey).toMutableSet() return LokiStorageAPI.shared.getAllDevicePublicKeysAsync(hexEncodedPublicKey).map { keys ->
val devices = keys.toMutableSet()
if (hexEncodedPublicKey != userHexEncodedPublicKey) { if (hexEncodedPublicKey != userHexEncodedPublicKey) {
devices.remove(userHexEncodedPublicKey) devices.remove(userHexEncodedPublicKey)
} }
val friends = getFriendPublicKeys(context, devices) val friends = getFriendPublicKeys(context, devices)
val map = mutableMapOf<String, Boolean>() val friendMap = mutableMapOf<String, Boolean>()
for (device in devices) { for (device in devices) {
map[device] = friends.contains(device) friendMap[device] = friends.contains(device)
} }
return map friendMap
}.toFailVoid()
} }
fun getFriendCount(context: Context, devices: Set<String>): Int { fun getFriendCount(context: Context, devices: Set<String>): Int {

View File

@ -39,6 +39,7 @@ class PushMessageSyncSendJob private constructor(
constructor(messageID: Long, recipient: Address, timestamp: Long, message: ByteArray, ttl: Int) : this(Parameters.Builder() constructor(messageID: Long, recipient: Address, timestamp: Long, message: ByteArray, ttl: Int) : this(Parameters.Builder()
.addConstraint(NetworkConstraint.KEY) .addConstraint(NetworkConstraint.KEY)
.setQueue(KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1)) .setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(3) .setMaxAttempts(3)
.build(), .build(),

View File

@ -22,7 +22,6 @@ import org.thoughtcrime.securesms.util.Hex
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.curve25519.Curve25519 import org.whispersystems.curve25519.Curve25519
import org.whispersystems.libsignal.util.KeyHelper import org.whispersystems.libsignal.util.KeyHelper
import org.whispersystems.signalservice.loki.api.LokiStorageAPI
import org.whispersystems.signalservice.loki.api.PairingAuthorisation import org.whispersystems.signalservice.loki.api.PairingAuthorisation
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
import org.whispersystems.signalservice.loki.utilities.Analytics import org.whispersystems.signalservice.loki.utilities.Analytics

View File

@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities; import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
import org.thoughtcrime.securesms.service.ExpiringMessageManager; import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.loki.api.LokiStorageAPI; import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
import java.util.LinkedList; import java.util.LinkedList;
@ -29,6 +30,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import kotlin.Unit; import kotlin.Unit;
import kotlin.contracts.Returns;
public class MarkReadReceiver extends BroadcastReceiver { public class MarkReadReceiver extends BroadcastReceiver {
@ -91,16 +93,17 @@ public class MarkReadReceiver extends BroadcastReceiver {
for (Address address : addressMap.keySet()) { for (Address address : addressMap.keySet()) {
List<Long> timestamps = Stream.of(addressMap.get(address)).map(SyncMessageId::getTimetamp).toList(); List<Long> timestamps = Stream.of(addressMap.get(address)).map(SyncMessageId::getTimetamp).toList();
MultiDeviceUtilities.getAllDevicePublicKeysWithFriendStatus(context, address.serialize()).success(devices -> {
Map<String, Boolean> devices = MultiDeviceUtilities.getAllDevicePublicKeysWithFriendStatus(context, address.serialize());
for (Map.Entry<String, Boolean> entry : devices.entrySet()) { for (Map.Entry<String, Boolean> entry : devices.entrySet()) {
String device = entry.getKey(); String device = entry.getKey();
boolean isFriend = entry.getValue(); boolean isFriend = entry.getValue();
// Loki - This also prevents read receipts from being sent in group chats as they don't maintain a friend request status // Loki - This also prevents read receipts from being sent in group chats as they don't maintain a friend request status
if (isFriend) { if (isFriend) {
ApplicationContext.getInstance(context).getJobManager().add(new SendReadReceiptJob(Address.fromSerialized(device), timestamps)); Util.runOnMain(() -> ApplicationContext.getInstance(context).getJobManager().add(new SendReadReceiptJob(Address.fromSerialized(device), timestamps)));
} }
} }
return Unit.INSTANCE;
});
} }
} }

View File

@ -72,6 +72,8 @@ public class MessageSender {
private static final String TAG = MessageSender.class.getSimpleName(); private static final String TAG = MessageSender.class.getSimpleName();
private enum MessageType { TEXT, MEDIA }
public static void sendBackgroundMessageToAllDevices(Context context, String contactHexEncodedPublicKey) { public static void sendBackgroundMessageToAllDevices(Context context, String contactHexEncodedPublicKey) {
// Send the background message to the original pubkey // Send the background message to the original pubkey
sendBackgroundMessage(context, contactHexEncodedPublicKey); sendBackgroundMessage(context, contactHexEncodedPublicKey);
@ -254,26 +256,32 @@ public class MessageSender {
} }
private static void sendTextPush(Context context, Recipient recipient, long messageId) { private static void sendTextPush(Context context, Recipient recipient, long messageId) {
sendMessagePush(context, MessageType.TEXT, recipient, messageId);
}
private static void sendMediaPush(Context context, Recipient recipient, long messageId) {
sendMessagePush(context, MessageType.MEDIA, recipient, messageId);
}
private static void sendMessagePush(Context context, MessageType type, Recipient recipient, long messageId) {
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
// Just send the message normally if it's a group message // 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(); String recipientPublicKey = recipient.getAddress().serialize();
if (GeneralUtilitiesKt.isPublicChat(context, recipientPublicKey)) { if (GeneralUtilitiesKt.isPublicChat(context, recipientPublicKey) || MultiDeviceUtilities.isOneOfOurDevices(context, recipient.getAddress())) {
if (type == MessageType.MEDIA) {
PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress(), false);
} else {
jobManager.add(new PushTextSendJob(messageId, recipient.getAddress())); jobManager.add(new PushTextSendJob(messageId, recipient.getAddress()));
}
return; return;
} }
// Note to self boolean[] hasSentSyncMessage = { false };
boolean isNoteToSelf = MultiDeviceUtilities.isOneOfOurDevices(context, recipient.getAddress()); MultiDeviceUtilities.getAllDevicePublicKeysWithFriendStatus(context, recipientPublicKey).success(devices -> {
if (isNoteToSelf) {
jobManager.add(new PushTextSendJob(messageId, recipient.getAddress()));
return;
}
boolean hasSentSyncMessage = false;
Map<String, Boolean> devices = MultiDeviceUtilities.getAllDevicePublicKeysWithFriendStatus(context, recipientPublicKey);
int friendCount = MultiDeviceUtilities.getFriendCount(context, devices.keySet()); int friendCount = MultiDeviceUtilities.getFriendCount(context, devices.keySet());
Util.runOnMain(() -> {
for (Map.Entry<String, Boolean> entry : devices.entrySet()) { for (Map.Entry<String, Boolean> entry : devices.entrySet()) {
String devicePublicKey = entry.getKey(); String devicePublicKey = entry.getKey();
boolean isFriend = entry.getValue(); boolean isFriend = entry.getValue();
@ -284,61 +292,28 @@ public class MessageSender {
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
// We should also send a sync message if we haven't already sent one // We should also send a sync message if we haven't already sent one
boolean shouldSendSyncMessage = !hasSentSyncMessage && MultiDeviceUtilities.shouldSendSycMessage(context, address); 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)); jobManager.add(new PushTextSendJob(messageId, messageIDToUse, address, shouldSendSyncMessage));
hasSentSyncMessage = 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;
if (type == MessageType.MEDIA) {
PushMediaSendJob.enqueue(context, jobManager, messageId, messageIDToUse, address, true, defaultFriendRequestMessage, false);
} else {
jobManager.add(new PushTextSendJob(messageId, messageIDToUse, address, true, defaultFriendRequestMessage, false)); jobManager.add(new PushTextSendJob(messageId, messageIDToUse, address, true, defaultFriendRequestMessage, false));
} }
} }
} }
});
private static void sendMediaPush(Context context, Recipient recipient, long messageId) { return Unit.INSTANCE;
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); });
// Just send the message normally if it's a group message
String recipientPublicKey = recipient.getAddress().serialize();
if (GeneralUtilitiesKt.isPublicChat(context, recipientPublicKey)) {
PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress(), false);
return;
}
// Note to self
boolean isNoteToSelf = MultiDeviceUtilities.isOneOfOurDevices(context, recipient.getAddress());
if (isNoteToSelf) {
jobManager.add(new PushTextSendJob(messageId, recipient.getAddress()));
return;
}
boolean hasSentSyncMessage = false;
Map<String, Boolean> devices = MultiDeviceUtilities.getAllDevicePublicKeysWithFriendStatus(context, recipientPublicKey);
int friendCount = MultiDeviceUtilities.getFriendCount(context, devices.keySet());
for (Map.Entry<String, Boolean> entry : devices.entrySet()) {
String devicePublicKey = entry.getKey();
boolean isFriend = entry.getValue();
Address address = Address.fromSerialized(devicePublicKey);
long messageIDToUse = recipientPublicKey.equals(devicePublicKey) ? messageId : -1L;
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 && MultiDeviceUtilities.shouldSendSycMessage(context, address);
PushMediaSendJob.enqueue(context, jobManager, messageId, messageIDToUse, address, shouldSendSyncMessage);
hasSentSyncMessage = shouldSendSyncMessage;
} 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.
boolean isFriendsWithAny = (friendCount > 0);
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, false);
}
}
} }
private static void sendGroupPush(Context context, Recipient recipient, long messageId, Address filterAddress) { private static void sendGroupPush(Context context, Recipient recipient, long messageId, Address filterAddress) {

View File

@ -640,7 +640,7 @@ public class TextSecurePreferences {
} }
public static void setLocalNumber(Context context, String localNumber) { public static void setLocalNumber(Context context, String localNumber) {
setStringPreference(context, LOCAL_NUMBER_PREF, localNumber); setStringPreference(context, LOCAL_NUMBER_PREF, localNumber.toLowerCase());
} }
public static void removeLocalNumber(Context context) { public static void removeLocalNumber(Context context) {