diff --git a/res/layout/view_device_linking.xml b/res/layout/view_device_linking.xml
index b16429e9b1..e9ecbc4229 100644
--- a/res/layout/view_device_linking.xml
+++ b/res/layout/view_device_linking.xml
@@ -1,6 +1,7 @@
+
+
Your device has been linked successfully
Authorize
Cancel
+ Device Name (Optional)
Scan QR Code
Scan the QR code of the person you\'d like to securely message. They can find their QR code by going into Loki Messenger\'s in-app settings and clicking \"Show QR Code\".
diff --git a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java
index 0d2438d79b..0bed4c6adb 100644
--- a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java
+++ b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java
@@ -26,7 +26,6 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Bundle;
@@ -40,14 +39,8 @@ import android.support.v7.app.AlertDialog;
import android.support.v7.preference.Preference;
import android.widget.Toast;
-import org.jetbrains.annotations.NotNull;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
-import org.thoughtcrime.securesms.database.DatabaseFactory;
-import org.thoughtcrime.securesms.loki.DeviceLinkingDialog;
-import org.thoughtcrime.securesms.loki.DeviceLinkingDialogDelegate;
-import org.thoughtcrime.securesms.loki.DeviceLinkingView;
import org.thoughtcrime.securesms.loki.LinkedDevicesActivity;
-import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
import org.thoughtcrime.securesms.loki.QRCodeDialog;
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment;
@@ -58,7 +51,6 @@ import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.whispersystems.signalservice.loki.api.PairingAuthorisation;
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec;
import org.whispersystems.signalservice.loki.utilities.Analytics;
import org.whispersystems.signalservice.loki.utilities.SerializationKt;
diff --git a/src/org/thoughtcrime/securesms/loki/DeviceLinkingDelegate.kt b/src/org/thoughtcrime/securesms/loki/DeviceLinkingDelegate.kt
new file mode 100644
index 0000000000..d7509a5c42
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/DeviceLinkingDelegate.kt
@@ -0,0 +1,33 @@
+package org.thoughtcrime.securesms.loki
+
+import org.whispersystems.signalservice.loki.api.PairingAuthorisation
+
+interface DeviceLinkingDelegate {
+ companion object {
+ fun combine(vararg delegates: DeviceLinkingDelegate?): DeviceLinkingDelegate {
+ val validDelegates = delegates.filterNotNull()
+ return object : DeviceLinkingDelegate {
+ override fun handleDeviceLinkAuthorized(pairingAuthorisation: PairingAuthorisation) {
+ for (delegate in validDelegates) { delegate.handleDeviceLinkAuthorized(pairingAuthorisation) }
+ }
+
+ override fun handleDeviceLinkingDialogDismissed() {
+ for (delegate in validDelegates) { delegate.handleDeviceLinkingDialogDismissed() }
+ }
+
+ override fun sendPairingAuthorizedMessage(pairingAuthorisation: PairingAuthorisation) {
+ for (delegate in validDelegates) { delegate.sendPairingAuthorizedMessage(pairingAuthorisation) }
+ }
+
+ override fun setDeviceDisplayName(hexEncodedPublicKey: String, displayName: String) {
+ for (delegate in validDelegates) { delegate.setDeviceDisplayName(hexEncodedPublicKey, displayName) }
+ }
+ }
+ }
+ }
+
+ fun handleDeviceLinkAuthorized(pairingAuthorisation: PairingAuthorisation) {}
+ fun handleDeviceLinkingDialogDismissed() {}
+ fun sendPairingAuthorizedMessage(pairingAuthorisation: PairingAuthorisation) {}
+ fun setDeviceDisplayName(hexEncodedPublicKey: String, displayName: String) {}
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/DeviceLinkingDialog.kt b/src/org/thoughtcrime/securesms/loki/DeviceLinkingDialog.kt
index d5faaaca16..0b6bede2eb 100644
--- a/src/org/thoughtcrime/securesms/loki/DeviceLinkingDialog.kt
+++ b/src/org/thoughtcrime/securesms/loki/DeviceLinkingDialog.kt
@@ -8,13 +8,12 @@ import org.whispersystems.signalservice.loki.api.DeviceLinkingSession
import org.whispersystems.signalservice.loki.api.DeviceLinkingSessionListener
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
-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: DeviceLinkingDelegate?) : DeviceLinkingDelegate, DeviceLinkingSessionListener {
private lateinit var view: DeviceLinkingView
private lateinit var dialog: AlertDialog
companion object {
-
- fun show(context: Context, mode: DeviceLinkingView.Mode, delegate: DeviceLinkingDialogDelegate?): DeviceLinkingDialog {
+ fun show(context: Context, mode: DeviceLinkingView.Mode, delegate: DeviceLinkingDelegate?): DeviceLinkingDialog {
val dialog = DeviceLinkingDialog(context, mode, delegate)
dialog.show()
return dialog
@@ -22,8 +21,10 @@ class DeviceLinkingDialog private constructor(private val context: Context, priv
}
private fun show() {
- view = DeviceLinkingView(context, mode, this)
+ val delegate = DeviceLinkingDelegate.combine(this, this.delegate)
+ view = DeviceLinkingView(context, mode, delegate)
dialog = AlertDialog.Builder(context).setView(view).show()
+ dialog.setCanceledOnTouchOutside(false)
view.dismiss = { dismiss() }
DeviceLinkingSession.shared.startListeningForLinkingRequests()
DeviceLinkingSession.shared.addListener(this)
@@ -35,20 +36,11 @@ class DeviceLinkingDialog private constructor(private val context: Context, priv
dialog.dismiss()
}
- override fun handleDeviceLinkAuthorized(pairingAuthorisation: PairingAuthorisation) {
- delegate?.handleDeviceLinkAuthorized(pairingAuthorisation)
- }
-
override fun handleDeviceLinkingDialogDismissed() {
if (mode == DeviceLinkingView.Mode.Master && view.pairingAuthorisation != null) {
val authorisation = view.pairingAuthorisation!!
DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(authorisation.secondaryDevicePublicKey)
}
- delegate?.handleDeviceLinkingDialogDismissed()
- }
-
- override fun sendPairingAuthorizedMessage(pairingAuthorisation: PairingAuthorisation) {
- delegate?.sendPairingAuthorizedMessage(pairingAuthorisation)
}
override fun requestUserAuthorization(authorisation: PairingAuthorisation) {
diff --git a/src/org/thoughtcrime/securesms/loki/DeviceLinkingDialogDelegate.kt b/src/org/thoughtcrime/securesms/loki/DeviceLinkingDialogDelegate.kt
deleted file mode 100644
index 5083b013b3..0000000000
--- a/src/org/thoughtcrime/securesms/loki/DeviceLinkingDialogDelegate.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.thoughtcrime.securesms.loki
-
-import org.whispersystems.signalservice.loki.api.PairingAuthorisation
-
-interface DeviceLinkingDialogDelegate {
- fun handleDeviceLinkAuthorized(pairingAuthorisation: PairingAuthorisation) { }
- fun handleDeviceLinkingDialogDismissed() { }
- fun sendPairingAuthorizedMessage(pairingAuthorisation: PairingAuthorisation) { }
-}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/DeviceLinkingView.kt b/src/org/thoughtcrime/securesms/loki/DeviceLinkingView.kt
index 35c5041d7e..e797a99e06 100644
--- a/src/org/thoughtcrime/securesms/loki/DeviceLinkingView.kt
+++ b/src/org/thoughtcrime/securesms/loki/DeviceLinkingView.kt
@@ -4,6 +4,8 @@ import android.content.Context
import android.graphics.Color
import android.graphics.PorterDuff
import android.os.Handler
+import android.text.Editable
+import android.text.TextWatcher
import android.util.AttributeSet
import android.view.View
import android.widget.LinearLayout
@@ -12,12 +14,10 @@ import network.loki.messenger.R
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
-import org.whispersystems.signalservice.loki.utilities.removing05PrefixIfNeeded
import java.io.File
-import java.io.FileOutputStream
-class DeviceLinkingView private constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, private val mode: Mode, private var delegate: DeviceLinkingViewDelegate) : LinearLayout(context, attrs, defStyleAttr) {
- private lateinit var languageFileDirectory: File
+class DeviceLinkingView private constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, private val mode: Mode, private var delegate: DeviceLinkingDelegate) : LinearLayout(context, attrs, defStyleAttr) {
+ private val languageFileDirectory: File = MnemonicUtilities.getLanguageFileDirectory(context)
var dismiss: (() -> Unit)? = null
var pairingAuthorisation: PairingAuthorisation? = null
private set
@@ -27,12 +27,11 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
// endregion
// 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
+ constructor(context: Context, mode: Mode, delegate: DeviceLinkingDelegate) : this(context, null, 0, mode, delegate)
+ private constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0, Mode.Master, object : DeviceLinkingDelegate { }) // Just pass in a dummy mode
private constructor(context: Context) : this(context, null)
init {
- languageFileDirectory = MnemonicUtilities.getLanguageFileDirectory(context)
setUpViewHierarchy()
}
@@ -57,6 +56,30 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
authorizeButton.visibility = View.GONE
authorizeButton.setOnClickListener { authorizePairing() }
cancelButton.setOnClickListener { cancel() }
+
+ deviceNameText.visibility = View.GONE
+ deviceNameText.input.addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
+ override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
+ override fun afterTextChanged(s: Editable?) {
+ val string = s?.toString() ?: ""
+ when {
+ string.trim().length > 30 -> {
+ deviceNameText.input.error = "Too Long"
+ enableAuthorizeButton(false)
+ }
+ else -> {
+ deviceNameText.input.error = null
+ enableAuthorizeButton(true)
+ }
+ }
+ }
+ })
+ }
+
+ private fun enableAuthorizeButton(enabled: Boolean) {
+ authorizeButton.isEnabled = enabled
+ authorizeButton.alpha = if (enabled) 1f else 0.5f
}
// endregion
@@ -71,9 +94,10 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
titleTextView.text = resources.getString(R.string.view_device_linking_title_3)
explanationTextView.text = resources.getString(R.string.view_device_linking_explanation_2)
mnemonicTextView.visibility = View.VISIBLE
- val hexEncodedPublicKey = pairingAuthorisation.secondaryDevicePublicKey.removing05PrefixIfNeeded()
- mnemonicTextView.text = MnemonicCodec(languageFileDirectory).encode(hexEncodedPublicKey).split(" ").slice(0 until 3).joinToString(" ")
+ mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), pairingAuthorisation.secondaryDevicePublicKey)
authorizeButton.visibility = View.VISIBLE
+ deviceNameText.visibility = View.VISIBLE
+ enableAuthorizeButton(true)
}
fun onDeviceLinkAuthorized(pairingAuthorisation: PairingAuthorisation) {
@@ -105,6 +129,7 @@ class DeviceLinkingView private constructor(context: Context, attrs: AttributeSe
if (mode != Mode.Master || pairingAuthorisation == null) { return; }
delegate.sendPairingAuthorizedMessage(pairingAuthorisation)
delegate.handleDeviceLinkAuthorized(pairingAuthorisation)
+ delegate.setDeviceDisplayName(pairingAuthorisation.secondaryDevicePublicKey, deviceNameText.text.toString().trim())
dismiss?.invoke()
}
diff --git a/src/org/thoughtcrime/securesms/loki/DeviceLinkingViewDelegate.kt b/src/org/thoughtcrime/securesms/loki/DeviceLinkingViewDelegate.kt
deleted file mode 100644
index 56c77bd9c7..0000000000
--- a/src/org/thoughtcrime/securesms/loki/DeviceLinkingViewDelegate.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.thoughtcrime.securesms.loki
-
-import org.whispersystems.signalservice.loki.api.PairingAuthorisation
-
-interface DeviceLinkingViewDelegate {
-
- fun handleDeviceLinkAuthorized(pairingAuthorisation: PairingAuthorisation) { }
- fun handleDeviceLinkingDialogDismissed() { }
- fun sendPairingAuthorizedMessage(pairingAuthorisation: PairingAuthorisation) { }
-}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/LinkedDevicesActivity.kt b/src/org/thoughtcrime/securesms/loki/LinkedDevicesActivity.kt
index a0fd9ae183..4414101900 100644
--- a/src/org/thoughtcrime/securesms/loki/LinkedDevicesActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/LinkedDevicesActivity.kt
@@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.loki
+import android.os.AsyncTask
import android.os.Bundle
import android.view.MenuItem
import android.widget.Toast
@@ -10,10 +11,11 @@ import network.loki.messenger.R
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.TextSecurePreferences
+import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.loki.api.LokiStorageAPI
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
-class LinkedDevicesActivity : PassphraseRequiredActionBarActivity(), DeviceLinkingDialogDelegate {
+class LinkedDevicesActivity : PassphraseRequiredActionBarActivity(), DeviceLinkingDelegate {
companion object {
private val TAG = DeviceActivity::class.java.simpleName
@@ -68,7 +70,13 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity(), DeviceLinki
}
override fun sendPairingAuthorizedMessage(pairingAuthorisation: PairingAuthorisation) {
- signAndSendPairingAuthorisationMessage(this, pairingAuthorisation)
- this.deviceListFragment.refresh()
+ AsyncTask.execute {
+ signAndSendPairingAuthorisationMessage(this, pairingAuthorisation)
+ Util.runOnMain { this.deviceListFragment.refresh() }
+ }
+ }
+
+ override fun setDeviceDisplayName(hexEncodedPublicKey: String, displayName: String) {
+ DatabaseFactory.getLokiUserDatabase(this).setDisplayName(hexEncodedPublicKey, displayName)
}
}
diff --git a/src/org/thoughtcrime/securesms/loki/SeedActivity.kt b/src/org/thoughtcrime/securesms/loki/SeedActivity.kt
index 87954908a6..04c788bf5e 100644
--- a/src/org/thoughtcrime/securesms/loki/SeedActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/SeedActivity.kt
@@ -32,7 +32,7 @@ import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
import java.io.File
import java.io.FileOutputStream
-class SeedActivity : BaseActionBarActivity(), DeviceLinkingDialogDelegate {
+class SeedActivity : BaseActionBarActivity(), DeviceLinkingDelegate {
private lateinit var languageFileDirectory: File
private var mode = Mode.Register
set(newValue) { field = newValue; updateUI() }