Partially implement feedback

This commit is contained in:
Niels Andriesse 2019-10-07 16:15:06 +11:00
parent 19ec4db687
commit 742d9bfa46
14 changed files with 111 additions and 162 deletions

View file

@ -432,9 +432,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
public void setUpStorageAPIIfNeeded() {
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
if (userHexEncodedPublicKey != null && IdentityKeyUtil.hasIdentityKey(this)) {
boolean isDebugMode = BuildConfig.DEBUG;
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
LokiAPIDatabaseProtocol database = DatabaseFactory.getLokiAPIDatabase(this);
boolean isDebugMode = BuildConfig.DEBUG;
LokiStorageAPI.Companion.configure(isDebugMode, userHexEncodedPublicKey, userPrivateKey, database);
}
}

View file

@ -335,7 +335,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
QRCodeDialog.INSTANCE.show(getContext());
break;
case PREFERENCE_CATEGORY_LINK_DEVICE:
DeviceLinkingDialog.Companion.show(getContext(), DeviceLinkingView.Mode.Master);
DeviceLinkingDialog.Companion.show(getContext(), DeviceLinkingView.Mode.Master, null);
break;
case PREFERENCE_CATEGORY_SEED:
Analytics.Companion.getShared().track("Seed Modal Shown");

View file

@ -125,7 +125,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(LokiAPIDatabase.getCreateGroupChatAuthTokenTableCommand());
db.execSQL(LokiAPIDatabase.getCreateLastMessageServerIDTableCommand());
db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand());
db.execSQL(LokiAPIDatabase.getCreateMultiDeviceAuthTableCommand());
db.execSQL(LokiAPIDatabase.getCreatePairingAuthorisationTableCommand());
db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand());
db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand());
db.execSQL(LokiMessageDatabase.getCreateTableCommand());
@ -496,7 +496,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
}
if (oldVersion < lokiV3) {
db.execSQL(LokiAPIDatabase.getCreateMultiDeviceAuthTableCommand());
db.execSQL(LokiAPIDatabase.getCreatePairingAuthorisationTableCommand());
}
db.setTransactionSuccessful();

View file

@ -12,14 +12,14 @@ import org.whispersystems.signalservice.loki.api.LokiStorageAPI
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
class DeviceLinkingDialog private constructor(private val context: Context, private val mode: DeviceLinkingView.Mode, private val delegate: DeviceLinkingDialogDelegate? = null): DeviceLinkingViewDelegate, DeviceLinkingSessionListener {
class DeviceLinkingDialog private constructor(private val context: Context, private val mode: DeviceLinkingView.Mode, private val delegate: DeviceLinkingDialogDelegate?) : DeviceLinkingViewDelegate, DeviceLinkingSessionListener {
private lateinit var view: DeviceLinkingView
private lateinit var dialog: AlertDialog
private val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
companion object {
fun show(context: Context, mode: DeviceLinkingView.Mode): DeviceLinkingDialog { return show(context, mode, null) }
fun show(context: Context, mode: DeviceLinkingView.Mode, delegate: DeviceLinkingDialogDelegate?): DeviceLinkingDialog {
val dialog = DeviceLinkingDialog(context, mode, delegate)
dialog.show()
@ -27,20 +27,18 @@ class DeviceLinkingDialog private constructor(private val context: Context, priv
}
}
public fun dismiss() {
this.stopListening()
dialog.dismiss()
}
private fun show() {
view = DeviceLinkingView(context, mode, this)
dialog = AlertDialog.Builder(context).setView(view).show()
view.dismiss = { dismiss() }
this.startListening()
startListening()
}
public fun dismiss() {
stopListening()
dialog.dismiss()
}
// region Private functions
private fun startListening() {
DeviceLinkingSession.shared.startListeningForLinkingRequests()
DeviceLinkingSession.shared.addListener(this)
@ -50,29 +48,20 @@ class DeviceLinkingDialog private constructor(private val context: Context, priv
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
DeviceLinkingSession.shared.removeListener(this)
}
// endregion
// region Dialog View Delegate
override fun authorise(pairing: PairingAuthorisation): Boolean {
override fun sendPairingAuthorizedMessage(pairing: PairingAuthorisation): Boolean {
val signedAuthorisation = pairing.sign(PairingAuthorisation.Type.GRANT, userPrivateKey)
if (signedAuthorisation == null || signedAuthorisation.type != PairingAuthorisation.Type.GRANT) {
Log.e("Loki", "Failed to sign grant authorisation")
Log.d("Loki", "Failed to sign pairing authorization.")
return false
}
// Send authorisation message
retryIfNeeded(3) {
sendAuthorisationMessage(context, pairing.secondaryDevicePublicKey, signedAuthorisation)
retryIfNeeded(8) {
sendPairingAuthorisationMessage(context, pairing.secondaryDevicePublicKey, signedAuthorisation).get()
}.fail {
Log.e("Loki", "Failed to send GRANT authorisation to ${pairing.secondaryDevicePublicKey}")
Log.d("Loki", "Failed to send pairing authorization message to ${pairing.secondaryDevicePublicKey}.")
}
// Add the auth to the database
DatabaseFactory.getLokiAPIDatabase(context).insertOrUpdatePairingAuthorisation(signedAuthorisation)
// Update the api
LokiStorageAPI.shared?.updateUserDeviceMappings()
LokiStorageAPI.shared.updateUserDeviceMappings()
return true
}
@ -81,24 +70,17 @@ class DeviceLinkingDialog private constructor(private val context: Context, priv
}
override fun handleDeviceLinkingDialogDismissed() {
// If we cancelled while we were listening for requests on main device, we need to remove any pre key bundles
if (mode == DeviceLinkingView.Mode.Master && view.pairingAuthorisation != null) {
val authorisation = view.pairingAuthorisation!!
// Remove pre key bundle from the requesting device
DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(authorisation.secondaryDevicePublicKey)
}
delegate?.handleDeviceLinkingDialogDismissed()
}
// endregion
// region Loki Device Session Listener
override fun requestUserAuthorization(authorisation: PairingAuthorisation) {
Util.runOnMain {
view.requestUserAuthorization(authorisation)
}
// Stop listening to any more requests
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
}
@ -106,9 +88,6 @@ class DeviceLinkingDialog private constructor(private val context: Context, priv
Util.runOnMain {
view.onDeviceLinkAuthorized(authorisation)
}
// Stop listening to any more requests
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
}
// endregion
}

View file

@ -1,12 +1,6 @@
package org.thoughtcrime.securesms.loki
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
interface DeviceLinkingDialogDelegate {
fun handleDeviceLinkAuthorized() {}
fun handleDeviceLinkingDialogDismissed() {}
}
interface DeviceLinkingViewDelegate: DeviceLinkingDialogDelegate {
fun authorise(pairing: PairingAuthorisation): Boolean { return false }
fun handleDeviceLinkAuthorized() { }
fun handleDeviceLinkingDialogDismissed() { }
}

View file

@ -5,7 +5,6 @@ import android.graphics.Color
import android.graphics.PorterDuff
import android.os.Handler
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.widget.LinearLayout
import kotlinx.android.synthetic.main.view_device_linking.view.*
@ -29,7 +28,7 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
// region Lifecycle
constructor(context: Context, mode: Mode, delegate: DeviceLinkingViewDelegate) : this(context, null, 0, mode, delegate)
private constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0, Mode.Master, object : DeviceLinkingViewDelegate {}) // Just pass in a dummy mode
private constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0, Mode.Master, object : DeviceLinkingViewDelegate { }) // Just pass in a dummy mode
private constructor(context: Context) : this(context, null)
init {
@ -77,29 +76,17 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
mnemonicTextView.text = MnemonicCodec(languageFileDirectory).encode(hexEncodedPublicKey).split(" ").slice(0 until 3).joinToString(" ")
}
authorizeButton.visibility = View.GONE
authorizeButton.setOnClickListener { authorize() }
authorizeButton.setOnClickListener { authorizePairing() }
cancelButton.setOnClickListener { cancel() }
}
// endregion
// region Device Linking
fun requestUserAuthorization(authorisation: PairingAuthorisation) {
// To be called when a linking request has been received
if (mode != Mode.Master) {
Log.w("Loki", "Received request for pairing authorisation on a slave device")
return
}
if (authorisation.type != PairingAuthorisation.Type.REQUEST) {
Log.w("Loki", "Received request for GRANT pairing authorisation! It shouldn't be possible!!")
return
}
if (this.pairingAuthorisation != null) {
Log.e("Loki", "Received request for another pairing authorisation when one was active")
return
}
if (mode != Mode.Master) { throw IllegalStateException() }
if (authorisation.type != PairingAuthorisation.Type.REQUEST) { throw IllegalStateException() }
if (pairingAuthorisation != null) { return }
pairingAuthorisation = authorisation
spinner.visibility = View.GONE
val titleTextViewLayoutParams = titleTextView.layoutParams as LayoutParams
titleTextViewLayoutParams.topMargin = toPx(16, resources)
@ -110,25 +97,11 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
val hexEncodedPublicKey = authorisation.secondaryDevicePublicKey.removing05PrefixIfNeeded()
mnemonicTextView.text = MnemonicCodec(languageFileDirectory).encode(hexEncodedPublicKey).split(" ").slice(0 until 3).joinToString(" ")
authorizeButton.visibility = View.VISIBLE
this.pairingAuthorisation = authorisation
}
private fun authorize() {
if (pairingAuthorisation == null || mode != Mode.Master ) { return; }
// Pass authorisation to delegate and only dismiss if it succeeded
if (delegate.authorise(pairingAuthorisation!!)) {
delegate.handleDeviceLinkAuthorized()
dismiss?.invoke()
}
}
fun onDeviceLinkAuthorized(authorisation: PairingAuthorisation) {
// To be called when a device link was accepted by the primary device
if (mode == Mode.Master || pairingAuthorisation != null) { return }
if (mode != Mode.Slave || pairingAuthorisation != null) { return }
pairingAuthorisation = authorisation
spinner.visibility = View.GONE
val titleTextViewLayoutParams = titleTextView.layoutParams as LayoutParams
titleTextViewLayoutParams.topMargin = toPx(8, resources)
@ -142,7 +115,6 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
mnemonicTextView.visibility = View.GONE
buttonContainer.visibility = View.GONE
cancelButton.visibility = View.GONE
Handler().postDelayed({
delegate.handleDeviceLinkAuthorized()
dismiss?.invoke()
@ -151,6 +123,14 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
// endregion
// region Interaction
private fun authorizePairing() {
if (pairingAuthorisation == null || mode != Mode.Master ) { return; }
if (delegate.sendPairingAuthorizedMessage(pairingAuthorisation!!)) {
delegate.handleDeviceLinkAuthorized()
dismiss?.invoke()
}
}
private fun cancel() {
delegate.handleDeviceLinkingDialogDismissed()
dismiss?.invoke()

View file

@ -0,0 +1,9 @@
package org.thoughtcrime.securesms.loki
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
interface DeviceLinkingViewDelegate {
fun handleDeviceLinkAuthorized() { }
fun handleDeviceLinkingDialogDismissed() { }
fun sendPairingAuthorizedMessage(pairing: PairingAuthorisation): Boolean { return false }
}

View file

@ -1,8 +1,14 @@
package org.thoughtcrime.securesms.loki
import android.content.Context
import android.content.res.Resources
import android.os.Build
import android.support.annotation.ColorRes
import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.recipients.Recipient
import org.whispersystems.signalservice.loki.api.LokiGroupChatAPI
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
import kotlin.math.roundToInt
fun Resources.getColorWithID(@ColorRes id: Int, theme: Resources.Theme?): Int {
@ -16,4 +22,20 @@ fun Resources.getColorWithID(@ColorRes id: Int, theme: Resources.Theme?): Int {
fun toPx(dp: Int, resources: Resources): Int {
val scale = resources.displayMetrics.density
return (dp * scale).roundToInt()
}
fun isGroupRecipient(recipient: String): Boolean {
return (LokiGroupChatAPI.publicChatServer == recipient)
}
fun getFriendPublicKeys(context: Context, devicePublicKeys: Set<String>): Set<String> {
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
return devicePublicKeys.mapNotNull { device ->
val address = Address.fromSerialized(device)
val recipient = Recipient.from(context, address, false)
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient)
if (threadID < 0) { return@mapNotNull null }
val friendRequestStatus = lokiThreadDatabase.getFriendRequestStatus(threadID)
if (friendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS) device else null
}.toSet()
}

View file

@ -47,14 +47,14 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
private val lastDeletionServerIDCacheIndex = "loki_api_last_deletion_server_id_cache_index"
private val lastDeletionServerID = "last_deletion_server_id"
@JvmStatic val createLastDeletionServerIDTableCommand = "CREATE TABLE $lastDeletionServerIDCache ($lastDeletionServerIDCacheIndex STRING PRIMARY KEY, $lastDeletionServerID INTEGER DEFAULT 0);"
// Authorisation
private val multiDeviceAuthTable = "loki_multi_device_authorisation"
private val primaryDevice = "primary_device"
private val secondaryDevice = "secondary_device"
// Pairing authorisation cache
private val pairingAuthorisationCache = "loki_pairing_authorisation_cache"
private val primaryDevicePublicKey = "primary_device"
private val secondaryDevicePublicKey = "secondary_device"
private val requestSignature = "request_signature"
private val grantSignature = "grant_signature"
@JvmStatic val createMultiDeviceAuthTableCommand = "CREATE TABLE $multiDeviceAuthTable($primaryDevice TEXT, $secondaryDevice TEXT, $requestSignature TEXT NULLABLE DEFAULT NULL, $grantSignature TEXT NULLABLE DEFAULT NULL, PRIMARY KEY ($primaryDevice, $secondaryDevice));"
@JvmStatic val createPairingAuthorisationTableCommand = "CREATE TABLE $pairingAuthorisationCache ($primaryDevicePublicKey TEXT, $secondaryDevicePublicKey TEXT, " +
"$requestSignature TEXT NULLABLE DEFAULT NULL, $grantSignature TEXT NULLABLE DEFAULT NULL, PRIMARY KEY ($primaryDevicePublicKey, $secondaryDevicePublicKey));"
}
override fun getSwarmCache(hexEncodedPublicKey: String): Set<LokiAPITarget>? {
@ -154,9 +154,9 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
override fun getPairingAuthorisations(hexEncodedPublicKey: String): List<PairingAuthorisation> {
val database = databaseHelper.readableDatabase
return database.getAll(multiDeviceAuthTable, "$primaryDevice = ? OR $secondaryDevice = ?", arrayOf(hexEncodedPublicKey, hexEncodedPublicKey)) { cursor ->
val primaryDevicePubKey = cursor.getString(primaryDevice)
val secondaryDevicePubKey = cursor.getString(secondaryDevice)
return database.getAll(pairingAuthorisationCache, "$primaryDevicePublicKey = ? OR $secondaryDevicePublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) { cursor ->
val primaryDevicePubKey = cursor.getString(primaryDevicePublicKey)
val secondaryDevicePubKey = cursor.getString(secondaryDevicePublicKey)
val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature)
val grantSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(grantSignature))) null else cursor.getBase64EncodedData(grantSignature)
PairingAuthorisation(primaryDevicePubKey, secondaryDevicePubKey, requestSignature, grantSignature)
@ -166,16 +166,16 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
override fun insertOrUpdatePairingAuthorisation(authorisation: PairingAuthorisation) {
val database = databaseHelper.writableDatabase
val values = ContentValues()
values.put(primaryDevice, authorisation.primaryDevicePublicKey)
values.put(secondaryDevice, authorisation.secondaryDevicePublicKey)
values.put(primaryDevicePublicKey, authorisation.primaryDevicePublicKey)
values.put(secondaryDevicePublicKey, authorisation.secondaryDevicePublicKey)
if (authorisation.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(authorisation.requestSignature)) }
if (authorisation.grantSignature != null) { values.put(grantSignature, Base64.encodeBytes(authorisation.grantSignature)) }
database.insertOrUpdate(multiDeviceAuthTable, values, "$primaryDevice = ? AND $secondaryDevice = ?", arrayOf(authorisation.primaryDevicePublicKey, authorisation.secondaryDevicePublicKey))
database.insertOrUpdate(pairingAuthorisationCache, values, "$primaryDevicePublicKey = ? AND $secondaryDevicePublicKey = ?", arrayOf( authorisation.primaryDevicePublicKey, authorisation.secondaryDevicePublicKey ))
}
override fun removePairingAuthorisations(hexEncodedPublicKey: String) {
val database = databaseHelper.readableDatabase
database.delete(multiDeviceAuthTable, "$primaryDevice = ? OR $secondaryDevice = ?", arrayOf(hexEncodedPublicKey, hexEncodedPublicKey))
database.delete(pairingAuthorisationCache, "$primaryDevicePublicKey = ? OR $secondaryDevicePublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey ))
}
}

View file

@ -28,7 +28,7 @@ fun getAllDevices(context: Context, pubKey: String, storageAPI: LokiStorageAPI,
devices.remove(ourPubKey)
}
val friends = getFriends(context, devices)
val friends = getFriendPublicKeys(context, devices)
for (device in devices) {
block(device, friends.contains(device), friends.count())
}
@ -80,7 +80,7 @@ fun shouldAutomaticallyBecomeFriendsWithDevice(pubKey: String, context: Context)
return deferred.promise
}
fun sendAuthorisationMessage(context: Context, contactHexEncodedPublicKey: String, authorisation: PairingAuthorisation): Promise<Unit, Exception> {
fun sendPairingAuthorisationMessage(context: Context, contactHexEncodedPublicKey: String, authorisation: PairingAuthorisation): Promise<Unit, Exception> {
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
val address = SignalServiceAddress(contactHexEncodedPublicKey)
val message = SignalServiceDataMessage.newBuilder().withBody("").withPairingAuthorisation(authorisation)

View file

@ -233,7 +233,7 @@ class SeedActivity : BaseActionBarActivity() {
// Send the request to the other user
CoroutineScope(Dispatchers.Main).launch {
retryIfNeeded(3) {
sendAuthorisationMessage(this@SeedActivity, authorisation.primaryDevicePublicKey, authorisation).get()
sendPairingAuthorisationMessage(this@SeedActivity, authorisation.primaryDevicePublicKey, authorisation).get()
}.failUi {
dialog.dismiss()
resetRegistration()

View file

@ -1,25 +0,0 @@
package org.thoughtcrime.securesms.loki
import android.content.Context
import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.recipients.Recipient
import org.whispersystems.signalservice.loki.api.LokiGroupChatAPI
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
fun isGroupChat(pubKey: String): Boolean {
return (LokiGroupChatAPI.publicChatServer == pubKey)
}
fun getFriends(context: Context, devices: Set<String>): Set<String> {
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
return devices.mapNotNull { device ->
val address = Address.fromSerialized(device)
val recipient = Recipient.from(context, address, false)
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient)
if (threadID < 0) { return@mapNotNull null }
if (lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) device else null
}.toSet()
}

View file

@ -20,11 +20,9 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilKt;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
import java.util.LinkedList;
import java.util.List;
@ -91,20 +89,13 @@ public class MarkReadReceiver extends BroadcastReceiver {
for (Address address : addressMap.keySet()) {
LokiStorageAPI storageAPI = LokiStorageAPI.Companion.getShared();
if (storageAPI == null) {
Log.w("Loki", "LokiStorageAPI is not initialized!");
return;
}
List<Long> timestamps = Stream.of(addressMap.get(address)).map(SyncMessageId::getTimetamp).toList();
MultiDeviceUtilKt.getAllDevices(context, address.serialize(), storageAPI, (devicePubKey, isFriend, friendCount) -> {
MultiDeviceUtilKt.getAllDevices(context, address.serialize(), storageAPI, (devicePublicKey, isFriend, friendCount) -> {
// Loki - This also prevents read receipts from being sent in group chats as they don't maintain a friend request status
if (isFriend) {
Address deviceAddress = Address.fromSerialized(devicePubKey);
ApplicationContext.getInstance(context)
.getJobManager()
.add(new SendReadReceiptJob(deviceAddress, timestamps));
ApplicationContext.getInstance(context).getJobManager().add(new SendReadReceiptJob(Address.fromSerialized(devicePublicKey), timestamps));
}
return Unit.INSTANCE;
});

View file

@ -42,8 +42,8 @@ import org.thoughtcrime.securesms.jobs.SmsSendJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.GeneralUtilitiesKt;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilKt;
import org.thoughtcrime.securesms.loki.UtilKt;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.push.AccountManagerFactory;
@ -211,27 +211,26 @@ public class MessageSender {
LokiStorageAPI storageAPI = LokiStorageAPI.Companion.getShared();
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
// Just send the message normally if the storage api is not set or if it's a group message
String recipientPubKey = recipient.getAddress().serialize();
if (storageAPI == null || UtilKt.isGroupChat(recipientPubKey)) {
if (storageAPI == null) { Log.w("Loki", "LokiStorageAPI is not initialized!"); }
// Just send the message normally if it's a group message
String recipientPublicKey = recipient.getAddress().serialize();
if (GeneralUtilitiesKt.isGroupRecipient(recipientPublicKey)) {
jobManager.add(new PushTextSendJob(messageId, recipient.getAddress()));
return;
}
MultiDeviceUtilKt.getAllDevices(context, recipientPubKey, storageAPI, (devicePubKey, isFriend, friendCount) -> {
Address deviceAddress = Address.fromSerialized(devicePubKey);
long messageIdToUse = recipientPubKey.equals(devicePubKey) ? messageId : -1L;
MultiDeviceUtilKt.getAllDevices(context, recipientPublicKey, storageAPI, (devicePublicKey, isFriend, friendCount) -> {
Address address = Address.fromSerialized(devicePublicKey);
long messageIDToUse = recipientPublicKey.equals(devicePublicKey) ? messageId : -1L;
// Send a normal message to our friends
if (isFriend) {
jobManager.add(new PushTextSendJob(messageId, messageIdToUse, deviceAddress));
// Send a normal message if the user is friends with the recipient
jobManager.add(new PushTextSendJob(messageId, messageIDToUse, address));
} else {
// Send friend requests to non friends
// If we're friends with one of the devices then send out a default friend request message
boolean isFriendsWithAny = friendCount > 0;
String defaultFriendRequestMessage = isFriendsWithAny ? "This is a friend request for devices linked to " + recipientPubKey : null;
jobManager.add(new PushTextSendJob(messageId, messageIdToUse, deviceAddress, true, defaultFriendRequestMessage));
// 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));
}
return Unit.INSTANCE;
@ -242,26 +241,26 @@ public class MessageSender {
LokiStorageAPI storageAPI = LokiStorageAPI.Companion.getShared();
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
// Just send the message normally if the storage api is not set or if it's a group message
String recipientPubKey = recipient.getAddress().serialize();
if (storageAPI == null || UtilKt.isGroupChat(recipientPubKey)) {
if (storageAPI == null) { Log.w("Loki", "LokiStorageAPI is not initialized!"); }
// Just send the message normally if it's a group message
String recipientPublicKey = recipient.getAddress().serialize();
if (GeneralUtilitiesKt.isGroupRecipient(recipientPublicKey)) {
PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress());
return;
}
MultiDeviceUtilKt.getAllDevices(context, recipientPubKey, storageAPI, (devicePubKey, isFriend, friendCount) -> {
Address deviceAddress = Address.fromSerialized(devicePubKey);
long messageIdToUse = recipientPubKey.equals(devicePubKey) ? messageId : -1L;
MultiDeviceUtilKt.getAllDevices(context, recipientPublicKey, storageAPI, (devicePublicKey, isFriend, friendCount) -> {
Address address = Address.fromSerialized(devicePublicKey);
long messageIDToUse = recipientPublicKey.equals(devicePublicKey) ? messageId : -1L;
// Send a normal message to our friends
if (isFriend) {
PushMediaSendJob.enqueue(context, jobManager, messageId, messageIdToUse, deviceAddress);
// Send a normal message if the user is friends with the recipient
PushMediaSendJob.enqueue(context, jobManager, messageId, messageIDToUse, address);
} else {
// Send friend requests to non friends
// If we're friends with one of the devices then send out a default friend request message
// 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 ? "This is a friend request for devices linked to " + recipientPubKey : null;
PushMediaSendJob.enqueue(context, jobManager, messageId, messageIdToUse, deviceAddress, true, defaultFriendRequestMessage);
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);
}
return Unit.INSTANCE;