mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 20:15:21 +00:00
Partially implement feedback
This commit is contained in:
parent
19ec4db687
commit
742d9bfa46
@ -432,9 +432,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
public void setUpStorageAPIIfNeeded() {
|
public void setUpStorageAPIIfNeeded() {
|
||||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
|
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||||
if (userHexEncodedPublicKey != null && IdentityKeyUtil.hasIdentityKey(this)) {
|
if (userHexEncodedPublicKey != null && IdentityKeyUtil.hasIdentityKey(this)) {
|
||||||
|
boolean isDebugMode = BuildConfig.DEBUG;
|
||||||
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
|
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
|
||||||
LokiAPIDatabaseProtocol database = DatabaseFactory.getLokiAPIDatabase(this);
|
LokiAPIDatabaseProtocol database = DatabaseFactory.getLokiAPIDatabase(this);
|
||||||
boolean isDebugMode = BuildConfig.DEBUG;
|
|
||||||
LokiStorageAPI.Companion.configure(isDebugMode, userHexEncodedPublicKey, userPrivateKey, database);
|
LokiStorageAPI.Companion.configure(isDebugMode, userHexEncodedPublicKey, userPrivateKey, database);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,7 +335,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
|||||||
QRCodeDialog.INSTANCE.show(getContext());
|
QRCodeDialog.INSTANCE.show(getContext());
|
||||||
break;
|
break;
|
||||||
case PREFERENCE_CATEGORY_LINK_DEVICE:
|
case PREFERENCE_CATEGORY_LINK_DEVICE:
|
||||||
DeviceLinkingDialog.Companion.show(getContext(), DeviceLinkingView.Mode.Master);
|
DeviceLinkingDialog.Companion.show(getContext(), DeviceLinkingView.Mode.Master, null);
|
||||||
break;
|
break;
|
||||||
case PREFERENCE_CATEGORY_SEED:
|
case PREFERENCE_CATEGORY_SEED:
|
||||||
Analytics.Companion.getShared().track("Seed Modal Shown");
|
Analytics.Companion.getShared().track("Seed Modal Shown");
|
||||||
|
@ -125,7 +125,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
db.execSQL(LokiAPIDatabase.getCreateGroupChatAuthTokenTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreateGroupChatAuthTokenTableCommand());
|
||||||
db.execSQL(LokiAPIDatabase.getCreateLastMessageServerIDTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreateLastMessageServerIDTableCommand());
|
||||||
db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand());
|
||||||
db.execSQL(LokiAPIDatabase.getCreateMultiDeviceAuthTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreatePairingAuthorisationTableCommand());
|
||||||
db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand());
|
db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand());
|
||||||
db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand());
|
db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand());
|
||||||
db.execSQL(LokiMessageDatabase.getCreateTableCommand());
|
db.execSQL(LokiMessageDatabase.getCreateTableCommand());
|
||||||
@ -496,7 +496,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (oldVersion < lokiV3) {
|
if (oldVersion < lokiV3) {
|
||||||
db.execSQL(LokiAPIDatabase.getCreateMultiDeviceAuthTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreatePairingAuthorisationTableCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
|
@ -12,14 +12,14 @@ 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.utilities.retryIfNeeded
|
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 view: DeviceLinkingView
|
||||||
private lateinit var dialog: AlertDialog
|
private lateinit var dialog: AlertDialog
|
||||||
|
|
||||||
private val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
|
private val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
|
||||||
|
|
||||||
companion object {
|
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 {
|
fun show(context: Context, mode: DeviceLinkingView.Mode, delegate: DeviceLinkingDialogDelegate?): DeviceLinkingDialog {
|
||||||
val dialog = DeviceLinkingDialog(context, mode, delegate)
|
val dialog = DeviceLinkingDialog(context, mode, delegate)
|
||||||
dialog.show()
|
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() {
|
private fun show() {
|
||||||
view = DeviceLinkingView(context, mode, this)
|
view = DeviceLinkingView(context, mode, this)
|
||||||
dialog = AlertDialog.Builder(context).setView(view).show()
|
dialog = AlertDialog.Builder(context).setView(view).show()
|
||||||
view.dismiss = { dismiss() }
|
view.dismiss = { dismiss() }
|
||||||
|
startListening()
|
||||||
this.startListening()
|
}
|
||||||
|
|
||||||
|
public fun dismiss() {
|
||||||
|
stopListening()
|
||||||
|
dialog.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
// region Private functions
|
|
||||||
private fun startListening() {
|
private fun startListening() {
|
||||||
DeviceLinkingSession.shared.startListeningForLinkingRequests()
|
DeviceLinkingSession.shared.startListeningForLinkingRequests()
|
||||||
DeviceLinkingSession.shared.addListener(this)
|
DeviceLinkingSession.shared.addListener(this)
|
||||||
@ -50,29 +48,20 @@ class DeviceLinkingDialog private constructor(private val context: Context, priv
|
|||||||
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
||||||
DeviceLinkingSession.shared.removeListener(this)
|
DeviceLinkingSession.shared.removeListener(this)
|
||||||
}
|
}
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Dialog View Delegate
|
override fun sendPairingAuthorizedMessage(pairing: PairingAuthorisation): Boolean {
|
||||||
override fun authorise(pairing: PairingAuthorisation): Boolean {
|
|
||||||
val signedAuthorisation = pairing.sign(PairingAuthorisation.Type.GRANT, userPrivateKey)
|
val signedAuthorisation = pairing.sign(PairingAuthorisation.Type.GRANT, userPrivateKey)
|
||||||
if (signedAuthorisation == null || signedAuthorisation.type != PairingAuthorisation.Type.GRANT) {
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
retryIfNeeded(8) {
|
||||||
// Send authorisation message
|
sendPairingAuthorisationMessage(context, pairing.secondaryDevicePublicKey, signedAuthorisation).get()
|
||||||
retryIfNeeded(3) {
|
|
||||||
sendAuthorisationMessage(context, pairing.secondaryDevicePublicKey, signedAuthorisation)
|
|
||||||
}.fail {
|
}.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)
|
DatabaseFactory.getLokiAPIDatabase(context).insertOrUpdatePairingAuthorisation(signedAuthorisation)
|
||||||
|
LokiStorageAPI.shared.updateUserDeviceMappings()
|
||||||
// Update the api
|
|
||||||
LokiStorageAPI.shared?.updateUserDeviceMappings()
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,24 +70,17 @@ class DeviceLinkingDialog private constructor(private val context: Context, priv
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun handleDeviceLinkingDialogDismissed() {
|
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) {
|
if (mode == DeviceLinkingView.Mode.Master && view.pairingAuthorisation != null) {
|
||||||
val authorisation = view.pairingAuthorisation!!
|
val authorisation = view.pairingAuthorisation!!
|
||||||
// Remove pre key bundle from the requesting device
|
|
||||||
DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(authorisation.secondaryDevicePublicKey)
|
DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(authorisation.secondaryDevicePublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate?.handleDeviceLinkingDialogDismissed()
|
delegate?.handleDeviceLinkingDialogDismissed()
|
||||||
}
|
}
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Loki Device Session Listener
|
|
||||||
override fun requestUserAuthorization(authorisation: PairingAuthorisation) {
|
override fun requestUserAuthorization(authorisation: PairingAuthorisation) {
|
||||||
Util.runOnMain {
|
Util.runOnMain {
|
||||||
view.requestUserAuthorization(authorisation)
|
view.requestUserAuthorization(authorisation)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop listening to any more requests
|
|
||||||
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,9 +88,6 @@ class DeviceLinkingDialog private constructor(private val context: Context, priv
|
|||||||
Util.runOnMain {
|
Util.runOnMain {
|
||||||
view.onDeviceLinkAuthorized(authorisation)
|
view.onDeviceLinkAuthorized(authorisation)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop listening to any more requests
|
|
||||||
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
||||||
}
|
}
|
||||||
// endregion
|
|
||||||
}
|
}
|
@ -1,12 +1,6 @@
|
|||||||
package org.thoughtcrime.securesms.loki
|
package org.thoughtcrime.securesms.loki
|
||||||
|
|
||||||
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
|
|
||||||
|
|
||||||
interface DeviceLinkingDialogDelegate {
|
interface DeviceLinkingDialogDelegate {
|
||||||
fun handleDeviceLinkAuthorized() { }
|
fun handleDeviceLinkAuthorized() { }
|
||||||
fun handleDeviceLinkingDialogDismissed() { }
|
fun handleDeviceLinkingDialogDismissed() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeviceLinkingViewDelegate: DeviceLinkingDialogDelegate {
|
|
||||||
fun authorise(pairing: PairingAuthorisation): Boolean { return false }
|
|
||||||
}
|
|
@ -5,7 +5,6 @@ import android.graphics.Color
|
|||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import kotlinx.android.synthetic.main.view_device_linking.view.*
|
import kotlinx.android.synthetic.main.view_device_linking.view.*
|
||||||
@ -77,29 +76,17 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
|
|||||||
mnemonicTextView.text = MnemonicCodec(languageFileDirectory).encode(hexEncodedPublicKey).split(" ").slice(0 until 3).joinToString(" ")
|
mnemonicTextView.text = MnemonicCodec(languageFileDirectory).encode(hexEncodedPublicKey).split(" ").slice(0 until 3).joinToString(" ")
|
||||||
}
|
}
|
||||||
authorizeButton.visibility = View.GONE
|
authorizeButton.visibility = View.GONE
|
||||||
authorizeButton.setOnClickListener { authorize() }
|
authorizeButton.setOnClickListener { authorizePairing() }
|
||||||
cancelButton.setOnClickListener { cancel() }
|
cancelButton.setOnClickListener { cancel() }
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Device Linking
|
// region Device Linking
|
||||||
fun requestUserAuthorization(authorisation: PairingAuthorisation) {
|
fun requestUserAuthorization(authorisation: PairingAuthorisation) {
|
||||||
// To be called when a linking request has been received
|
if (mode != Mode.Master) { throw IllegalStateException() }
|
||||||
if (mode != Mode.Master) {
|
if (authorisation.type != PairingAuthorisation.Type.REQUEST) { throw IllegalStateException() }
|
||||||
Log.w("Loki", "Received request for pairing authorisation on a slave device")
|
if (pairingAuthorisation != null) { return }
|
||||||
return
|
pairingAuthorisation = authorisation
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
spinner.visibility = View.GONE
|
spinner.visibility = View.GONE
|
||||||
val titleTextViewLayoutParams = titleTextView.layoutParams as LayoutParams
|
val titleTextViewLayoutParams = titleTextView.layoutParams as LayoutParams
|
||||||
titleTextViewLayoutParams.topMargin = toPx(16, resources)
|
titleTextViewLayoutParams.topMargin = toPx(16, resources)
|
||||||
@ -110,25 +97,11 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
|
|||||||
val hexEncodedPublicKey = authorisation.secondaryDevicePublicKey.removing05PrefixIfNeeded()
|
val hexEncodedPublicKey = authorisation.secondaryDevicePublicKey.removing05PrefixIfNeeded()
|
||||||
mnemonicTextView.text = MnemonicCodec(languageFileDirectory).encode(hexEncodedPublicKey).split(" ").slice(0 until 3).joinToString(" ")
|
mnemonicTextView.text = MnemonicCodec(languageFileDirectory).encode(hexEncodedPublicKey).split(" ").slice(0 until 3).joinToString(" ")
|
||||||
authorizeButton.visibility = View.VISIBLE
|
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) {
|
fun onDeviceLinkAuthorized(authorisation: PairingAuthorisation) {
|
||||||
// To be called when a device link was accepted by the primary device
|
if (mode != Mode.Slave || pairingAuthorisation != null) { return }
|
||||||
if (mode == Mode.Master || pairingAuthorisation != null) { return }
|
|
||||||
pairingAuthorisation = authorisation
|
pairingAuthorisation = authorisation
|
||||||
|
|
||||||
spinner.visibility = View.GONE
|
spinner.visibility = View.GONE
|
||||||
val titleTextViewLayoutParams = titleTextView.layoutParams as LayoutParams
|
val titleTextViewLayoutParams = titleTextView.layoutParams as LayoutParams
|
||||||
titleTextViewLayoutParams.topMargin = toPx(8, resources)
|
titleTextViewLayoutParams.topMargin = toPx(8, resources)
|
||||||
@ -142,7 +115,6 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
|
|||||||
mnemonicTextView.visibility = View.GONE
|
mnemonicTextView.visibility = View.GONE
|
||||||
buttonContainer.visibility = View.GONE
|
buttonContainer.visibility = View.GONE
|
||||||
cancelButton.visibility = View.GONE
|
cancelButton.visibility = View.GONE
|
||||||
|
|
||||||
Handler().postDelayed({
|
Handler().postDelayed({
|
||||||
delegate.handleDeviceLinkAuthorized()
|
delegate.handleDeviceLinkAuthorized()
|
||||||
dismiss?.invoke()
|
dismiss?.invoke()
|
||||||
@ -151,6 +123,14 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
|
|||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Interaction
|
// region Interaction
|
||||||
|
private fun authorizePairing() {
|
||||||
|
if (pairingAuthorisation == null || mode != Mode.Master ) { return; }
|
||||||
|
if (delegate.sendPairingAuthorizedMessage(pairingAuthorisation!!)) {
|
||||||
|
delegate.handleDeviceLinkAuthorized()
|
||||||
|
dismiss?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun cancel() {
|
private fun cancel() {
|
||||||
delegate.handleDeviceLinkingDialogDismissed()
|
delegate.handleDeviceLinkingDialogDismissed()
|
||||||
dismiss?.invoke()
|
dismiss?.invoke()
|
||||||
|
@ -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 }
|
||||||
|
}
|
@ -1,8 +1,14 @@
|
|||||||
package org.thoughtcrime.securesms.loki
|
package org.thoughtcrime.securesms.loki
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.support.annotation.ColorRes
|
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
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
fun Resources.getColorWithID(@ColorRes id: Int, theme: Resources.Theme?): Int {
|
fun Resources.getColorWithID(@ColorRes id: Int, theme: Resources.Theme?): Int {
|
||||||
@ -17,3 +23,19 @@ fun toPx(dp: Int, resources: Resources): Int {
|
|||||||
val scale = resources.displayMetrics.density
|
val scale = resources.displayMetrics.density
|
||||||
return (dp * scale).roundToInt()
|
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()
|
||||||
|
}
|
@ -47,14 +47,14 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
|||||||
private val lastDeletionServerIDCacheIndex = "loki_api_last_deletion_server_id_cache_index"
|
private val lastDeletionServerIDCacheIndex = "loki_api_last_deletion_server_id_cache_index"
|
||||||
private val lastDeletionServerID = "last_deletion_server_id"
|
private val lastDeletionServerID = "last_deletion_server_id"
|
||||||
@JvmStatic val createLastDeletionServerIDTableCommand = "CREATE TABLE $lastDeletionServerIDCache ($lastDeletionServerIDCacheIndex STRING PRIMARY KEY, $lastDeletionServerID INTEGER DEFAULT 0);"
|
@JvmStatic val createLastDeletionServerIDTableCommand = "CREATE TABLE $lastDeletionServerIDCache ($lastDeletionServerIDCacheIndex STRING PRIMARY KEY, $lastDeletionServerID INTEGER DEFAULT 0);"
|
||||||
|
// Pairing authorisation cache
|
||||||
// Authorisation
|
private val pairingAuthorisationCache = "loki_pairing_authorisation_cache"
|
||||||
private val multiDeviceAuthTable = "loki_multi_device_authorisation"
|
private val primaryDevicePublicKey = "primary_device"
|
||||||
private val primaryDevice = "primary_device"
|
private val secondaryDevicePublicKey = "secondary_device"
|
||||||
private val secondaryDevice = "secondary_device"
|
|
||||||
private val requestSignature = "request_signature"
|
private val requestSignature = "request_signature"
|
||||||
private val grantSignature = "grant_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>? {
|
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> {
|
override fun getPairingAuthorisations(hexEncodedPublicKey: String): List<PairingAuthorisation> {
|
||||||
val database = databaseHelper.readableDatabase
|
val database = databaseHelper.readableDatabase
|
||||||
return database.getAll(multiDeviceAuthTable, "$primaryDevice = ? OR $secondaryDevice = ?", arrayOf(hexEncodedPublicKey, hexEncodedPublicKey)) { cursor ->
|
return database.getAll(pairingAuthorisationCache, "$primaryDevicePublicKey = ? OR $secondaryDevicePublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) { cursor ->
|
||||||
val primaryDevicePubKey = cursor.getString(primaryDevice)
|
val primaryDevicePubKey = cursor.getString(primaryDevicePublicKey)
|
||||||
val secondaryDevicePubKey = cursor.getString(secondaryDevice)
|
val secondaryDevicePubKey = cursor.getString(secondaryDevicePublicKey)
|
||||||
val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature)
|
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)
|
val grantSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(grantSignature))) null else cursor.getBase64EncodedData(grantSignature)
|
||||||
PairingAuthorisation(primaryDevicePubKey, secondaryDevicePubKey, requestSignature, grantSignature)
|
PairingAuthorisation(primaryDevicePubKey, secondaryDevicePubKey, requestSignature, grantSignature)
|
||||||
@ -166,16 +166,16 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
|||||||
override fun insertOrUpdatePairingAuthorisation(authorisation: PairingAuthorisation) {
|
override fun insertOrUpdatePairingAuthorisation(authorisation: PairingAuthorisation) {
|
||||||
val database = databaseHelper.writableDatabase
|
val database = databaseHelper.writableDatabase
|
||||||
val values = ContentValues()
|
val values = ContentValues()
|
||||||
values.put(primaryDevice, authorisation.primaryDevicePublicKey)
|
values.put(primaryDevicePublicKey, authorisation.primaryDevicePublicKey)
|
||||||
values.put(secondaryDevice, authorisation.secondaryDevicePublicKey)
|
values.put(secondaryDevicePublicKey, authorisation.secondaryDevicePublicKey)
|
||||||
if (authorisation.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(authorisation.requestSignature)) }
|
if (authorisation.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(authorisation.requestSignature)) }
|
||||||
if (authorisation.grantSignature != null) { values.put(grantSignature, Base64.encodeBytes(authorisation.grantSignature)) }
|
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) {
|
override fun removePairingAuthorisations(hexEncodedPublicKey: String) {
|
||||||
val database = databaseHelper.readableDatabase
|
val database = databaseHelper.readableDatabase
|
||||||
database.delete(multiDeviceAuthTable, "$primaryDevice = ? OR $secondaryDevice = ?", arrayOf(hexEncodedPublicKey, hexEncodedPublicKey))
|
database.delete(pairingAuthorisationCache, "$primaryDevicePublicKey = ? OR $secondaryDevicePublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey ))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ fun getAllDevices(context: Context, pubKey: String, storageAPI: LokiStorageAPI,
|
|||||||
devices.remove(ourPubKey)
|
devices.remove(ourPubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
val friends = getFriends(context, devices)
|
val friends = getFriendPublicKeys(context, devices)
|
||||||
for (device in devices) {
|
for (device in devices) {
|
||||||
block(device, friends.contains(device), friends.count())
|
block(device, friends.contains(device), friends.count())
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ fun shouldAutomaticallyBecomeFriendsWithDevice(pubKey: String, context: Context)
|
|||||||
return deferred.promise
|
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 messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
||||||
val address = SignalServiceAddress(contactHexEncodedPublicKey)
|
val address = SignalServiceAddress(contactHexEncodedPublicKey)
|
||||||
val message = SignalServiceDataMessage.newBuilder().withBody("").withPairingAuthorisation(authorisation)
|
val message = SignalServiceDataMessage.newBuilder().withBody("").withPairingAuthorisation(authorisation)
|
||||||
|
@ -233,7 +233,7 @@ class SeedActivity : BaseActionBarActivity() {
|
|||||||
// Send the request to the other user
|
// Send the request to the other user
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
retryIfNeeded(3) {
|
retryIfNeeded(3) {
|
||||||
sendAuthorisationMessage(this@SeedActivity, authorisation.primaryDevicePublicKey, authorisation).get()
|
sendPairingAuthorisationMessage(this@SeedActivity, authorisation.primaryDevicePublicKey, authorisation).get()
|
||||||
}.failUi {
|
}.failUi {
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
resetRegistration()
|
resetRegistration()
|
||||||
|
@ -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()
|
|
||||||
}
|
|
@ -20,11 +20,9 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
|||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
|
import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.loki.LokiThreadDatabase;
|
|
||||||
import org.thoughtcrime.securesms.loki.MultiDeviceUtilKt;
|
import org.thoughtcrime.securesms.loki.MultiDeviceUtilKt;
|
||||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -91,20 +89,13 @@ public class MarkReadReceiver extends BroadcastReceiver {
|
|||||||
|
|
||||||
for (Address address : addressMap.keySet()) {
|
for (Address address : addressMap.keySet()) {
|
||||||
LokiStorageAPI storageAPI = LokiStorageAPI.Companion.getShared();
|
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();
|
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
|
// 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) {
|
||||||
Address deviceAddress = Address.fromSerialized(devicePubKey);
|
ApplicationContext.getInstance(context).getJobManager().add(new SendReadReceiptJob(Address.fromSerialized(devicePublicKey), timestamps));
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new SendReadReceiptJob(deviceAddress, timestamps));
|
|
||||||
}
|
}
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
});
|
});
|
||||||
|
@ -42,8 +42,8 @@ import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
|||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.loki.GeneralUtilitiesKt;
|
||||||
import org.thoughtcrime.securesms.loki.MultiDeviceUtilKt;
|
import org.thoughtcrime.securesms.loki.MultiDeviceUtilKt;
|
||||||
import org.thoughtcrime.securesms.loki.UtilKt;
|
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||||
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
||||||
@ -211,27 +211,26 @@ public class MessageSender {
|
|||||||
LokiStorageAPI storageAPI = LokiStorageAPI.Companion.getShared();
|
LokiStorageAPI storageAPI = LokiStorageAPI.Companion.getShared();
|
||||||
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
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
|
// Just send the message normally if it's a group message
|
||||||
String recipientPubKey = recipient.getAddress().serialize();
|
String recipientPublicKey = recipient.getAddress().serialize();
|
||||||
if (storageAPI == null || UtilKt.isGroupChat(recipientPubKey)) {
|
if (GeneralUtilitiesKt.isGroupRecipient(recipientPublicKey)) {
|
||||||
if (storageAPI == null) { Log.w("Loki", "LokiStorageAPI is not initialized!"); }
|
|
||||||
jobManager.add(new PushTextSendJob(messageId, recipient.getAddress()));
|
jobManager.add(new PushTextSendJob(messageId, recipient.getAddress()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiDeviceUtilKt.getAllDevices(context, recipientPubKey, storageAPI, (devicePubKey, isFriend, friendCount) -> {
|
MultiDeviceUtilKt.getAllDevices(context, recipientPublicKey, storageAPI, (devicePublicKey, isFriend, friendCount) -> {
|
||||||
Address deviceAddress = Address.fromSerialized(devicePubKey);
|
Address address = Address.fromSerialized(devicePublicKey);
|
||||||
long messageIdToUse = recipientPubKey.equals(devicePubKey) ? messageId : -1L;
|
long messageIDToUse = recipientPublicKey.equals(devicePublicKey) ? messageId : -1L;
|
||||||
|
|
||||||
// Send a normal message to our friends
|
|
||||||
if (isFriend) {
|
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 {
|
} else {
|
||||||
// Send friend requests to non friends
|
// Send friend requests to non friends. If the user is friends with any
|
||||||
// If we're friends with one 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 ? "This is a friend request for devices linked to " + recipientPubKey : null;
|
String defaultFriendRequestMessage = isFriendsWithAny ? "Accept this friend request to enable messages to be synced across devices" : null;
|
||||||
jobManager.add(new PushTextSendJob(messageId, messageIdToUse, deviceAddress, true, defaultFriendRequestMessage));
|
jobManager.add(new PushTextSendJob(messageId, messageIDToUse, address, true, defaultFriendRequestMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
@ -242,26 +241,26 @@ public class MessageSender {
|
|||||||
LokiStorageAPI storageAPI = LokiStorageAPI.Companion.getShared();
|
LokiStorageAPI storageAPI = LokiStorageAPI.Companion.getShared();
|
||||||
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
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
|
// Just send the message normally if it's a group message
|
||||||
String recipientPubKey = recipient.getAddress().serialize();
|
String recipientPublicKey = recipient.getAddress().serialize();
|
||||||
if (storageAPI == null || UtilKt.isGroupChat(recipientPubKey)) {
|
if (GeneralUtilitiesKt.isGroupRecipient(recipientPublicKey)) {
|
||||||
if (storageAPI == null) { Log.w("Loki", "LokiStorageAPI is not initialized!"); }
|
|
||||||
PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress());
|
PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiDeviceUtilKt.getAllDevices(context, recipientPubKey, storageAPI, (devicePubKey, isFriend, friendCount) -> {
|
MultiDeviceUtilKt.getAllDevices(context, recipientPublicKey, storageAPI, (devicePublicKey, isFriend, friendCount) -> {
|
||||||
Address deviceAddress = Address.fromSerialized(devicePubKey);
|
Address address = Address.fromSerialized(devicePublicKey);
|
||||||
long messageIdToUse = recipientPubKey.equals(devicePubKey) ? messageId : -1L;
|
long messageIDToUse = recipientPublicKey.equals(devicePublicKey) ? messageId : -1L;
|
||||||
|
|
||||||
// Send a normal message to our friends
|
|
||||||
if (isFriend) {
|
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 {
|
} else {
|
||||||
// Send friend requests to non friends
|
// Send friend requests to non friends. If the user is friends with any
|
||||||
// If we're friends with one 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 ? "This is a friend request for devices linked to " + recipientPubKey : null;
|
String defaultFriendRequestMessage = isFriendsWithAny ? "Accept this friend request to enable messages to be synced across devices" : null;
|
||||||
PushMediaSendJob.enqueue(context, jobManager, messageId, messageIdToUse, deviceAddress, true, defaultFriendRequestMessage);
|
PushMediaSendJob.enqueue(context, jobManager, messageId, messageIDToUse, address, true, defaultFriendRequestMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
|
Loading…
Reference in New Issue
Block a user