Somewhat simplify device linking UI

This commit is contained in:
Niels Andriesse 2019-10-08 14:28:30 +11:00
parent a44c3fcd57
commit ac9c9f534e
6 changed files with 45 additions and 56 deletions

View File

@ -2,22 +2,16 @@ package org.thoughtcrime.securesms.loki
import android.content.Context import android.content.Context
import android.support.v7.app.AlertDialog import android.support.v7.app.AlertDialog
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.logging.Log
import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.loki.api.DeviceLinkingSession import org.whispersystems.signalservice.loki.api.DeviceLinkingSession
import org.whispersystems.signalservice.loki.api.DeviceLinkingSessionListener import org.whispersystems.signalservice.loki.api.DeviceLinkingSessionListener
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
class DeviceLinkingDialog private constructor(private val context: Context, private val mode: DeviceLinkingView.Mode, private val delegate: DeviceLinkingDialogDelegate?) : 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()
companion object { companion object {
fun show(context: Context, mode: DeviceLinkingView.Mode, delegate: DeviceLinkingDialogDelegate?): DeviceLinkingDialog { fun show(context: Context, mode: DeviceLinkingView.Mode, delegate: DeviceLinkingDialogDelegate?): DeviceLinkingDialog {
@ -31,42 +25,18 @@ class DeviceLinkingDialog private constructor(private val context: Context, priv
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()
}
public fun dismiss() {
stopListening()
dialog.dismiss()
}
private fun startListening() {
DeviceLinkingSession.shared.startListeningForLinkingRequests() DeviceLinkingSession.shared.startListeningForLinkingRequests()
DeviceLinkingSession.shared.addListener(this) DeviceLinkingSession.shared.addListener(this)
} }
private fun stopListening() { private fun dismiss() {
DeviceLinkingSession.shared.stopListeningForLinkingRequests() DeviceLinkingSession.shared.stopListeningForLinkingRequests()
DeviceLinkingSession.shared.removeListener(this) DeviceLinkingSession.shared.removeListener(this)
dialog.dismiss()
} }
override fun sendPairingAuthorizedMessage(pairing: PairingAuthorisation): Boolean { override fun handleDeviceLinkAuthorized(pairingAuthorisation: PairingAuthorisation) {
val signedAuthorisation = pairing.sign(PairingAuthorisation.Type.GRANT, userPrivateKey) delegate?.handleDeviceLinkAuthorized(pairingAuthorisation)
if (signedAuthorisation == null || signedAuthorisation.type != PairingAuthorisation.Type.GRANT) {
Log.d("Loki", "Failed to sign pairing authorization.")
return false
}
retryIfNeeded(8) {
sendPairingAuthorisationMessage(context, pairing.secondaryDevicePublicKey, signedAuthorisation).get()
}.fail {
Log.d("Loki", "Failed to send pairing authorization message to ${pairing.secondaryDevicePublicKey}.")
}
DatabaseFactory.getLokiAPIDatabase(context).insertOrUpdatePairingAuthorisation(signedAuthorisation)
LokiStorageAPI.shared.updateUserDeviceMappings()
return true
}
override fun handleDeviceLinkAuthorized(pairing: PairingAuthorisation) {
delegate?.handleDeviceLinkAuthorized(pairing)
} }
override fun handleDeviceLinkingDialogDismissed() { override fun handleDeviceLinkingDialogDismissed() {
@ -77,6 +47,10 @@ class DeviceLinkingDialog private constructor(private val context: Context, priv
delegate?.handleDeviceLinkingDialogDismissed() delegate?.handleDeviceLinkingDialogDismissed()
} }
override fun sendPairingAuthorizedMessage(pairingAuthorisation: PairingAuthorisation) {
delegate?.sendPairingAuthorizedMessage(pairingAuthorisation)
}
override fun requestUserAuthorization(authorisation: PairingAuthorisation) { override fun requestUserAuthorization(authorisation: PairingAuthorisation) {
Util.runOnMain { Util.runOnMain {
view.requestUserAuthorization(authorisation) view.requestUserAuthorization(authorisation)

View File

@ -4,6 +4,7 @@ import org.whispersystems.signalservice.loki.api.PairingAuthorisation
interface DeviceLinkingDialogDelegate { interface DeviceLinkingDialogDelegate {
fun handleDeviceLinkAuthorized(pairing: PairingAuthorisation) { } fun handleDeviceLinkAuthorized(pairingAuthorisation: PairingAuthorisation) { }
fun handleDeviceLinkingDialogDismissed() { } fun handleDeviceLinkingDialogDismissed() { }
fun sendPairingAuthorizedMessage(pairingAuthorisation: PairingAuthorisation) { }
} }

View File

@ -82,11 +82,9 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
// endregion // endregion
// region Device Linking // region Device Linking
fun requestUserAuthorization(authorisation: PairingAuthorisation) { fun requestUserAuthorization(pairingAuthorisation: PairingAuthorisation) {
if (mode != Mode.Master) { throw IllegalStateException() } if (mode != Mode.Master || pairingAuthorisation.type != PairingAuthorisation.Type.REQUEST || this.pairingAuthorisation != null) { return }
if (authorisation.type != PairingAuthorisation.Type.REQUEST) { throw IllegalStateException() } this.pairingAuthorisation = pairingAuthorisation
if (pairingAuthorisation != null) { return }
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(16, resources) titleTextViewLayoutParams.topMargin = toPx(16, resources)
@ -94,14 +92,14 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
titleTextView.text = resources.getString(R.string.view_device_linking_title_3) titleTextView.text = resources.getString(R.string.view_device_linking_title_3)
explanationTextView.text = resources.getString(R.string.view_device_linking_explanation_2) explanationTextView.text = resources.getString(R.string.view_device_linking_explanation_2)
mnemonicTextView.visibility = View.VISIBLE mnemonicTextView.visibility = View.VISIBLE
val hexEncodedPublicKey = authorisation.secondaryDevicePublicKey.removing05PrefixIfNeeded() val hexEncodedPublicKey = pairingAuthorisation.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
} }
fun onDeviceLinkAuthorized(authorisation: PairingAuthorisation) { fun onDeviceLinkAuthorized(pairingAuthorisation: PairingAuthorisation) {
if (mode != Mode.Slave || pairingAuthorisation != null) { return } if (mode != Mode.Slave || pairingAuthorisation.type != PairingAuthorisation.Type.GRANT || this.pairingAuthorisation != null) { return }
pairingAuthorisation = authorisation this.pairingAuthorisation = pairingAuthorisation
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)
@ -116,7 +114,7 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
buttonContainer.visibility = View.GONE buttonContainer.visibility = View.GONE
cancelButton.visibility = View.GONE cancelButton.visibility = View.GONE
Handler().postDelayed({ Handler().postDelayed({
delegate.handleDeviceLinkAuthorized(authorisation) delegate.handleDeviceLinkAuthorized(pairingAuthorisation)
dismiss?.invoke() dismiss?.invoke()
}, 4000) }, 4000)
} }
@ -124,12 +122,12 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
// region Interaction // region Interaction
private fun authorizePairing() { private fun authorizePairing() {
if (pairingAuthorisation == null || mode != Mode.Master ) { return; } val pairingAuthorisation = this.pairingAuthorisation
if (delegate.sendPairingAuthorizedMessage(pairingAuthorisation!!)) { if (mode != Mode.Master || pairingAuthorisation == null) { return; }
delegate.handleDeviceLinkAuthorized(pairingAuthorisation!!) delegate.sendPairingAuthorizedMessage(pairingAuthorisation)
delegate.handleDeviceLinkAuthorized(pairingAuthorisation)
dismiss?.invoke() dismiss?.invoke()
} }
}
private fun cancel() { private fun cancel() {
delegate.handleDeviceLinkingDialogDismissed() delegate.handleDeviceLinkingDialogDismissed()

View File

@ -4,7 +4,7 @@ import org.whispersystems.signalservice.loki.api.PairingAuthorisation
interface DeviceLinkingViewDelegate { interface DeviceLinkingViewDelegate {
fun handleDeviceLinkAuthorized(pairing: PairingAuthorisation) { } fun handleDeviceLinkAuthorized(pairingAuthorisation: PairingAuthorisation) { }
fun handleDeviceLinkingDialogDismissed() { } fun handleDeviceLinkingDialogDismissed() { }
fun sendPairingAuthorizedMessage(pairing: PairingAuthorisation): Boolean { return false } fun sendPairingAuthorizedMessage(pairingAuthorisation: PairingAuthorisation) { }
} }

View File

@ -57,7 +57,6 @@ fun shouldAutomaticallyBecomeFriendsWithDevice(publicKey: String, context: Conte
} }
deferred.resolve(lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) deferred.resolve(lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS)
} }
return deferred.promise return deferred.promise
} }

View File

@ -25,6 +25,7 @@ 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
@ -217,10 +218,10 @@ class SeedActivity : BaseActionBarActivity(), DeviceLinkingDialogDelegate {
} }
} }
override fun handleDeviceLinkAuthorized(pairing: PairingAuthorisation) { override fun handleDeviceLinkAuthorized(pairingAuthorisation: PairingAuthorisation) {
Analytics.shared.track("Device Linked Successfully") Analytics.shared.track("Device Linked Successfully")
if (pairing.secondaryDevicePublicKey == TextSecurePreferences.getLocalNumber(this)) { if (pairingAuthorisation.secondaryDevicePublicKey == TextSecurePreferences.getLocalNumber(this)) {
TextSecurePreferences.setMasterHexEncodedPublicKey(this, pairing.primaryDevicePublicKey) TextSecurePreferences.setMasterHexEncodedPublicKey(this, pairingAuthorisation.primaryDevicePublicKey)
} }
startActivity(Intent(this, ConversationListActivity::class.java)) startActivity(Intent(this, ConversationListActivity::class.java))
finish() finish()
@ -230,6 +231,22 @@ class SeedActivity : BaseActionBarActivity(), DeviceLinkingDialogDelegate {
resetForRegistration() resetForRegistration()
} }
override fun sendPairingAuthorizedMessage(pairingAuthorisation: PairingAuthorisation) {
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).privateKey.serialize()
val signedPairingAuthorisation = pairingAuthorisation.sign(PairingAuthorisation.Type.GRANT, userPrivateKey)
if (signedPairingAuthorisation == null || signedPairingAuthorisation.type != PairingAuthorisation.Type.GRANT) {
Log.d("Loki", "Failed to sign pairing authorization.")
return
}
retryIfNeeded(8) {
sendPairingAuthorisationMessage(this, pairingAuthorisation.secondaryDevicePublicKey, signedPairingAuthorisation).get()
}.fail {
Log.d("Loki", "Failed to send pairing authorization message to ${pairingAuthorisation.secondaryDevicePublicKey}.")
}
DatabaseFactory.getLokiAPIDatabase(this).insertOrUpdatePairingAuthorisation(signedPairingAuthorisation)
LokiStorageAPI.shared.updateUserDeviceMappings()
}
private fun resetForRegistration() { private fun resetForRegistration() {
IdentityKeyUtil.delete(this, IdentityKeyUtil.lokiSeedKey) IdentityKeyUtil.delete(this, IdentityKeyUtil.lokiSeedKey)
TextSecurePreferences.removeLocalRegistrationId(this) TextSecurePreferences.removeLocalRegistrationId(this)