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

View File

@ -249,7 +249,11 @@ import java.util.concurrent.atomic.AtomicInteger;
import kotlin.Unit;
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.database.GroupDatabase.GroupRecord;
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
@ -2185,44 +2189,50 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void handleThreadFriendRequestStatusChanged(long threadID) {
Util.runOnMain(() -> {
boolean shouldUpdateInputPanel = true;
if (threadID != this.threadId) {
Recipient threadRecipient = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID);
if (threadRecipient != null && !threadRecipient.isGroupRecipient()) {
// 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());
shouldUpdateInputPanel = devices.contains(recipient.getAddress().serialize());
} else {
shouldUpdateInputPanel = false;
LokiStorageAPI.shared.getAllDevicePublicKeysAsync(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();
}
return Unit.INSTANCE;
});
}
return;
}
if (shouldUpdateInputPanel) {
this.updateInputPanel();
}
});
this.updateInputPanel();
}
private void updateInputPanel() {
if (recipient.isGroupRecipient()) {
if (recipient.isGroupRecipient() || isNoteToSelf()) {
setInputPanelEnabled(true);
return;
}
task(() -> {
// 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();
inputPanel.setEnabled(true);
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);
boolean shouldEnableInput = isFriendsWithAnyLinkedDevices || !hasPendingFriendRequest;
updateToggleButtonState();
inputPanel.setEnabled(shouldEnableInput);
int hintID = shouldEnableInput ? R.string.activity_conversation_default_hint : R.string.activity_conversation_pending_friend_request_hint;
inputPanel.setHint(getResources().getString(hintID));
if (shouldEnableInput) {
inputPanel.composeText.requestFocus();
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(inputPanel.composeText, 0);
}
int hintID = enabled ? R.string.activity_conversation_default_hint : R.string.activity_conversation_pending_friend_request_hint;
inputPanel.setHint(getResources().getString(hintID));
inputPanel.setEnabled(enabled);
if (enabled) {
inputPanel.composeText.requestFocus();
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(inputPanel.composeText, 0);
}
});
}
private void sendMessage() {
@ -2428,7 +2438,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void updateToggleButtonState() {
// Don't allow attachments if we're not friends
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(this).getFriendRequestStatus(threadId);
if (!recipient.isGroupRecipient() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) {
if (!isNoteToSelf() && !recipient.isGroupRecipient() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) {
buttonToggle.display(sendButton);
quickAttachmentToggle.hide();
inlineAttachmentToggle.hide();
@ -3047,6 +3057,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
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) {
@ -3056,5 +3067,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
return false;
}
public boolean isNoteToSelf() {
return TextSecurePreferences.getLocalNumber(this).equalsIgnoreCase(recipient.getAddress().serialize());
}
// endregion
}

View File

@ -4,6 +4,8 @@ package org.thoughtcrime.securesms.loki
import android.content.Context
import android.os.Handler
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.crypto.IdentityKeyUtil
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.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> {
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
val keys = LokiStorageAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey)
@ -33,18 +39,20 @@ fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: Str
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 devices = LokiStorageAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey).toMutableSet()
if (hexEncodedPublicKey != userHexEncodedPublicKey) {
devices.remove(userHexEncodedPublicKey)
}
val friends = getFriendPublicKeys(context, devices)
val map = mutableMapOf<String, Boolean>()
for (device in devices) {
map[device] = friends.contains(device)
}
return map
return LokiStorageAPI.shared.getAllDevicePublicKeysAsync(hexEncodedPublicKey).map { keys ->
val devices = keys.toMutableSet()
if (hexEncodedPublicKey != userHexEncodedPublicKey) {
devices.remove(userHexEncodedPublicKey)
}
val friends = getFriendPublicKeys(context, devices)
val friendMap = mutableMapOf<String, Boolean>()
for (device in devices) {
friendMap[device] = friends.contains(device)
}
friendMap
}.toFailVoid()
}
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()
.addConstraint(NetworkConstraint.KEY)
.setQueue(KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(3)
.build(),

View File

@ -22,7 +22,6 @@ import org.thoughtcrime.securesms.util.Hex
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.curve25519.Curve25519
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.crypto.MnemonicCodec
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.loki.MultiDeviceUtilities;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
import java.util.LinkedList;
@ -29,6 +30,7 @@ import java.util.List;
import java.util.Map;
import kotlin.Unit;
import kotlin.contracts.Returns;
public class MarkReadReceiver extends BroadcastReceiver {
@ -91,16 +93,17 @@ public class MarkReadReceiver extends BroadcastReceiver {
for (Address address : addressMap.keySet()) {
List<Long> timestamps = Stream.of(addressMap.get(address)).map(SyncMessageId::getTimetamp).toList();
Map<String, Boolean> devices = MultiDeviceUtilities.getAllDevicePublicKeysWithFriendStatus(context, address.serialize());
for (Map.Entry<String, Boolean> entry : devices.entrySet()) {
String device = entry.getKey();
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
if (isFriend) {
ApplicationContext.getInstance(context).getJobManager().add(new SendReadReceiptJob(Address.fromSerialized(device), timestamps));
MultiDeviceUtilities.getAllDevicePublicKeysWithFriendStatus(context, address.serialize()).success(devices -> {
for (Map.Entry<String, Boolean> entry : devices.entrySet()) {
String device = entry.getKey();
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
if (isFriend) {
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 enum MessageType { TEXT, MEDIA }
public static void sendBackgroundMessageToAllDevices(Context context, String contactHexEncodedPublicKey) {
// Send the background message to the original pubkey
sendBackgroundMessage(context, contactHexEncodedPublicKey);
@ -254,91 +256,64 @@ public class MessageSender {
}
private static void sendTextPush(Context context, Recipient recipient, long messageId) {
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)) {
jobManager.add(new PushTextSendJob(messageId, recipient.getAddress()));
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);
jobManager.add(new PushTextSendJob(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;
jobManager.add(new PushTextSendJob(messageId, messageIDToUse, address, true, defaultFriendRequestMessage, false));
}
}
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();
// 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();
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;
if (GeneralUtilitiesKt.isPublicChat(context, recipientPublicKey) || MultiDeviceUtilities.isOneOfOurDevices(context, recipient.getAddress())) {
if (type == MessageType.MEDIA) {
PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress(), false);
} 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);
jobManager.add(new PushTextSendJob(messageId, recipient.getAddress()));
}
return;
}
boolean[] hasSentSyncMessage = { false };
MultiDeviceUtilities.getAllDevicePublicKeysWithFriendStatus(context, recipientPublicKey).success(devices -> {
int friendCount = MultiDeviceUtilities.getFriendCount(context, devices.keySet());
Util.runOnMain(() -> {
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[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));
}
hasSentSyncMessage[0] = 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;
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));
}
}
}
});
return Unit.INSTANCE;
});
}
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) {
setStringPreference(context, LOCAL_NUMBER_PREF, localNumber);
setStringPreference(context, LOCAL_NUMBER_PREF, localNumber.toLowerCase());
}
public static void removeLocalNumber(Context context) {