mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-25 01:07:47 +00:00
Implement device linking dialog
This commit is contained in:
parent
d8e86a5e42
commit
9f7437aa9e
@ -24,7 +24,7 @@
|
|||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:fontFamily="sans-serif-medium"
|
android:fontFamily="sans-serif-medium"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:text="@string/view_device_linking_title" />
|
android:text="@string/view_device_linking_title_1" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/explanationTextView"
|
android:id="@+id/explanationTextView"
|
||||||
@ -33,17 +33,47 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:text="@string/view_device_linking_explanation" />
|
android:text="@string/view_device_linking_explanation_1" />
|
||||||
|
|
||||||
<Button
|
<TextView
|
||||||
android:id="@+id/cancelButton"
|
android:id="@+id/mnemonicTextView"
|
||||||
|
style="@style/Signal.Text.Body"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="50dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:background="@color/transparent"
|
android:textAlignment="center"
|
||||||
android:textColor="@color/signal_primary"
|
android:text="word word word" />
|
||||||
android:text="@string/view_device_linking_cancel_button_title"
|
|
||||||
android:elevation="0dp"
|
<LinearLayout
|
||||||
android:stateListAnimator="@null" />
|
android:id="@+id/buttonContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/authorizeButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:textColor="@color/signal_primary"
|
||||||
|
android:text="@string/view_device_linking_authorize_button_title"
|
||||||
|
android:elevation="0dp"
|
||||||
|
android:stateListAnimator="@null" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/cancelButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:textColor="@color/signal_primary"
|
||||||
|
android:text="@string/view_device_linking_cancel_button_title"
|
||||||
|
android:elevation="0dp"
|
||||||
|
android:stateListAnimator="@null" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -1612,8 +1612,14 @@
|
|||||||
<string name="view_qr_code_explanation">This is your personal QR code. Other people can scan it to start a secure conversation with you.</string>
|
<string name="view_qr_code_explanation">This is your personal QR code. Other people can scan it to start a secure conversation with you.</string>
|
||||||
<string name="view_qr_code_cancel_button_title">Cancel</string>
|
<string name="view_qr_code_cancel_button_title">Cancel</string>
|
||||||
<!-- Device linking view -->
|
<!-- Device linking view -->
|
||||||
<string name="view_device_linking_title">Waiting for Device</string>
|
<string name="view_device_linking_title_1">Waiting for Device</string>
|
||||||
<string name="view_device_linking_explanation">Create a new account on your other device and click \"Link Device\" when you\'re at the \"Create Your Loki Messenger Account\" step to start the linking process</string>
|
<string name="view_device_linking_title_2">Waiting for Authorization</string>
|
||||||
|
<string name="view_device_linking_title_3">Linking Request Received</string>
|
||||||
|
<string name="view_device_linking_title_4">Device Link Authorized</string>
|
||||||
|
<string name="view_device_linking_explanation_1">Create a new account on your other device and click \"Link Device\" when you\'re at the \"Create Your Loki Messenger Account\" step to start the linking process</string>
|
||||||
|
<string name="view_device_linking_explanation_2">Please check that the words below match the ones shown on your other device</string>
|
||||||
|
<string name="view_device_linking_explanation_3">Your device has been linked successfully</string>
|
||||||
|
<string name="view_device_linking_authorize_button_title">Authorize</string>
|
||||||
<string name="view_device_linking_cancel_button_title">Cancel</string>
|
<string name="view_device_linking_cancel_button_title">Cancel</string>
|
||||||
<!-- Scan QR code fragment -->
|
<!-- Scan QR code fragment -->
|
||||||
<string name="fragment_scan_qr_code_title">Scan QR Code</string>
|
<string name="fragment_scan_qr_code_title">Scan QR Code</string>
|
||||||
|
@ -41,6 +41,7 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||||
import org.thoughtcrime.securesms.loki.DeviceLinkingDialog;
|
import org.thoughtcrime.securesms.loki.DeviceLinkingDialog;
|
||||||
|
import org.thoughtcrime.securesms.loki.DeviceLinkingView;
|
||||||
import org.thoughtcrime.securesms.loki.QRCodeDialog;
|
import org.thoughtcrime.securesms.loki.QRCodeDialog;
|
||||||
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
||||||
import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment;
|
import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment;
|
||||||
@ -327,7 +328,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.INSTANCE.show(getContext());
|
DeviceLinkingDialog.INSTANCE.show(getContext(), DeviceLinkingView.Mode.Master);
|
||||||
break;
|
break;
|
||||||
case PREFERENCE_CATEGORY_SEED:
|
case PREFERENCE_CATEGORY_SEED:
|
||||||
File languageFileDirectory = new File(getContext().getApplicationInfo().dataDir);
|
File languageFileDirectory = new File(getContext().getApplicationInfo().dataDir);
|
||||||
|
@ -3,66 +3,163 @@ package org.thoughtcrime.securesms.loki
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
|
import android.os.Handler
|
||||||
import android.support.v7.app.AlertDialog
|
import android.support.v7.app.AlertDialog
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
import android.util.Log
|
||||||
|
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.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
|
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
|
||||||
|
import org.whispersystems.signalservice.loki.utilities.removing05PrefixIfNeeded
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
|
||||||
object DeviceLinkingDialog {
|
object DeviceLinkingDialog {
|
||||||
|
|
||||||
fun show(context: Context, mode: DeviceLinkingView.Mode) {
|
fun show(context: Context, mode: DeviceLinkingView.Mode) {
|
||||||
val view = DeviceLinkingView(context, mode)
|
val view = DeviceLinkingView(context, mode)
|
||||||
val dialog = AlertDialog.Builder(context).setView(view).show()
|
val dialog = AlertDialog.Builder(context).setView(view).show()
|
||||||
view.onCancel = { dialog.dismiss() }
|
view.dismiss = { dialog.dismiss() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeviceLinkingView private constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) {
|
class DeviceLinkingView private constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, private val mode: Mode) : LinearLayout(context, attrs, defStyleAttr) {
|
||||||
private lateinit var mode: Mode
|
|
||||||
private var delegate: DeviceLinkingDialogDelegate? = null
|
private var delegate: DeviceLinkingDialogDelegate? = null
|
||||||
var onCancel: (() -> Unit)? = null
|
private lateinit var languageFileDirectory: File
|
||||||
|
var dismiss: (() -> Unit)? = null
|
||||||
|
|
||||||
// region Types
|
// region Types
|
||||||
enum class Mode { Master, Slave }
|
enum class Mode { Master, Slave }
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Lifecycle
|
// region Lifecycle
|
||||||
constructor(context: Context, mode: Mode) : this(context, null, 0) {
|
constructor(context: Context, mode: Mode) : this(context, null, 0, mode)
|
||||||
this.mode = mode
|
private constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0, Mode.Master) // Just pass in a dummy mode
|
||||||
}
|
|
||||||
private constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
|
||||||
private constructor(context: Context) : this(context, null)
|
private constructor(context: Context) : this(context, null)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (mode == Mode.Slave) {
|
if (mode == Mode.Slave) {
|
||||||
if (delegate == null) { throw IllegalStateException("Missing delegate for device linking dialog in slave mode.") }
|
if (delegate == null) { throw IllegalStateException("Missing delegate for device linking dialog in slave mode.") }
|
||||||
}
|
}
|
||||||
|
setUpLanguageFileDirectory()
|
||||||
setUpViewHierarchy()
|
setUpViewHierarchy()
|
||||||
when (mode) {
|
when (mode) {
|
||||||
Mode.Master -> throw IllegalStateException() // DeviceLinkingSession.startListeningForLinkingRequests(this)
|
Mode.Master -> Log.d("Loki", "TODO: DeviceLinkingSession.startListeningForLinkingRequests(this)")
|
||||||
Mode.Slave -> throw IllegalStateException() // DeviceLinkingSession.startListeningForAuthorization(this)
|
Mode.Slave -> Log.d("Loki", "TODO: DeviceLinkingSession.startListeningForAuthorization(this)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setUpLanguageFileDirectory() {
|
||||||
|
val languages = listOf( "english", "japanese", "portuguese", "spanish" )
|
||||||
|
val directory = File(context.applicationInfo.dataDir)
|
||||||
|
for (language in languages) {
|
||||||
|
val fileName = "$language.txt"
|
||||||
|
if (directory.list().contains(fileName)) { continue }
|
||||||
|
val inputStream = context.assets.open("mnemonic/$fileName")
|
||||||
|
val file = File(directory, fileName)
|
||||||
|
val outputStream = FileOutputStream(file)
|
||||||
|
val buffer = ByteArray(1024)
|
||||||
|
while (true) {
|
||||||
|
val count = inputStream.read(buffer)
|
||||||
|
if (count < 0) { break }
|
||||||
|
outputStream.write(buffer, 0, count)
|
||||||
|
}
|
||||||
|
inputStream.close()
|
||||||
|
outputStream.close()
|
||||||
|
}
|
||||||
|
languageFileDirectory = directory
|
||||||
|
}
|
||||||
|
|
||||||
private fun setUpViewHierarchy() {
|
private fun setUpViewHierarchy() {
|
||||||
inflate(context, R.layout.view_device_linking, this)
|
inflate(context, R.layout.view_device_linking, this)
|
||||||
spinner.indeterminateDrawable.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)
|
spinner.indeterminateDrawable.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)
|
||||||
cancelButton.setOnClickListener { onCancel?.invoke() }
|
val titleID = when (mode) {
|
||||||
|
Mode.Master -> R.string.view_device_linking_title_1
|
||||||
|
Mode.Slave -> R.string.view_device_linking_title_2
|
||||||
|
}
|
||||||
|
titleTextView.text = resources.getString(titleID)
|
||||||
|
val explanationID = when (mode) {
|
||||||
|
Mode.Master -> R.string.view_device_linking_explanation_1
|
||||||
|
Mode.Slave -> R.string.view_device_linking_explanation_2
|
||||||
|
}
|
||||||
|
explanationTextView.text = resources.getString(explanationID)
|
||||||
|
mnemonicTextView.visibility = if (mode == Mode.Master) View.GONE else View.VISIBLE
|
||||||
|
if (mode == Mode.Slave) {
|
||||||
|
val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context).removing05PrefixIfNeeded()
|
||||||
|
mnemonicTextView.text = MnemonicCodec(languageFileDirectory).encode(hexEncodedPublicKey).split(" ").slice(0 until 3).joinToString(" ")
|
||||||
|
}
|
||||||
|
authorizeButton.visibility = View.GONE
|
||||||
|
cancelButton.setOnClickListener { cancel() }
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Device Linking
|
// region Device Linking
|
||||||
private fun requestUserAuthorization() { // TODO: Device link
|
private fun requestUserAuthorization() { // TODO: deviceLink parameter
|
||||||
// Called by DeviceLinkingSession when a linking request has been received
|
// To be called by DeviceLinkingSession when a linking request has been received
|
||||||
|
// TODO: this.deviceLink = deviceLink
|
||||||
|
spinner.visibility = View.GONE
|
||||||
|
val titleTextViewLayoutParams = titleTextView.layoutParams as LayoutParams
|
||||||
|
titleTextViewLayoutParams.topMargin = toPx(16, resources)
|
||||||
|
titleTextView.layoutParams = titleTextViewLayoutParams
|
||||||
|
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 = TextSecurePreferences.getLocalNumber(context).removing05PrefixIfNeeded() // TODO: deviceLink.slave.hexEncodedPublicKey.removing05PrefixIfNeeded()
|
||||||
|
mnemonicTextView.text = MnemonicCodec(languageFileDirectory).encode(hexEncodedPublicKey).split(" ").slice(0 until 3).joinToString(" ")
|
||||||
|
authorizeButton.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun authorizeDeviceLink() {
|
private fun authorizeDeviceLink() {
|
||||||
|
// TODO: val deviceLink = this.deviceLink!!
|
||||||
|
// TODO: val linkingAuthorizationMessage = DeviceLinkingUtilities.getLinkingAuthorizationMessage(deviceLink)
|
||||||
|
// TODO: Send the linking authorization message
|
||||||
|
// TODO: val session = DeviceLinkingSession.current!!
|
||||||
|
// TODO: session.stopListeningForLinkingRequests()
|
||||||
|
// TODO: session.markLinkingRequestAsProcessed()
|
||||||
|
dismiss?.invoke()
|
||||||
|
// TODO: val master = DeviceLink.Device(deviceLink.master.hexEncodedPublicKey, linkingAuthorizationMessage.masterSignature)
|
||||||
|
// TODO: val signedDeviceLink = DeviceLink(master, deviceLink.slave)
|
||||||
|
// TODO: LokiStorageAPI.addDeviceLink(signedDeviceLink).fail { error ->
|
||||||
|
// TODO: Log.d("Loki", "Failed to add device link due to error: $error.")
|
||||||
|
// TODO: }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDeviceLinkAuthorized() { // TODO: Device link
|
private fun handleDeviceLinkAuthorized() { // TODO: deviceLink parameter
|
||||||
// Called by DeviceLinkingSession when a device link has been authorized
|
// To be called by DeviceLinkingSession when a device link has been authorized
|
||||||
|
// TODO: val session = DeviceLinkingSession.current!!
|
||||||
|
// TODO: session.stopListeningForLinkingAuthorization()
|
||||||
|
spinner.visibility = View.GONE
|
||||||
|
val titleTextViewLayoutParams = titleTextView.layoutParams as LayoutParams
|
||||||
|
titleTextViewLayoutParams.topMargin = toPx(8, resources)
|
||||||
|
titleTextView.layoutParams = titleTextViewLayoutParams
|
||||||
|
titleTextView.text = resources.getString(R.string.view_device_linking_title_4)
|
||||||
|
val explanationTextViewLayoutParams = explanationTextView.layoutParams as LayoutParams
|
||||||
|
explanationTextViewLayoutParams.bottomMargin = toPx(12, resources)
|
||||||
|
explanationTextView.layoutParams = explanationTextViewLayoutParams
|
||||||
|
explanationTextView.text = resources.getString(R.string.view_device_linking_explanation_3)
|
||||||
|
titleTextView.text = resources.getString(R.string.view_device_linking_title_4)
|
||||||
|
mnemonicTextView.visibility = View.GONE
|
||||||
|
buttonContainer.visibility = View.GONE
|
||||||
|
// TODO: LokiStorageAPI.addDeviceLink(signedDeviceLink).fail { error ->
|
||||||
|
// TODO: Log.d("Loki", "Failed to add device link due to error: $error.")
|
||||||
|
// TODO: }
|
||||||
|
Handler().postDelayed({
|
||||||
|
delegate?.handleDeviceLinkAuthorized()
|
||||||
|
dismiss?.invoke()
|
||||||
|
}, 4000)
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Interaction
|
||||||
|
private fun cancel() {
|
||||||
|
// TODO: val session = DeviceLinkingSession.current!!
|
||||||
|
// TODO: session.stopListeningForLinkingRequests()
|
||||||
|
// TODO: session.markLinkingRequestAsProcessed() // Only relevant in master mode
|
||||||
|
delegate?.handleDeviceLinkingDialogDismissed() // Only relevant in slave mode
|
||||||
|
dismiss?.invoke()
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user