You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-android/src/org/thoughtcrime/securesms/loki/DeviceLinkingView.kt

132 lines
6.5 KiB
Kotlin

package org.thoughtcrime.securesms.loki
import android.content.Context
import android.graphics.Color
import android.graphics.PorterDuff
import android.os.Handler
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.view.View
import android.widget.LinearLayout
import kotlinx.android.synthetic.main.view_device_linking.view.*
import kotlinx.android.synthetic.main.view_device_linking.view.cancelButton
import kotlinx.android.synthetic.main.view_device_linking.view.explanationTextView
import kotlinx.android.synthetic.main.view_device_linking.view.titleTextView
import network.loki.messenger.R
import org.thoughtcrime.securesms.qr.QrCode
import org.thoughtcrime.securesms.util.ServiceUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
import java.io.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
// region Types
enum class Mode { Master, Slave }
// endregion
// region Lifecycle
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 {
setUpViewHierarchy()
}
private fun setUpViewHierarchy() {
inflate(context, R.layout.view_device_linking, this)
spinner.indeterminateDrawable.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)
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)
mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), hexEncodedPublicKey)
}
authorizeButton.visibility = View.GONE
authorizeButton.setOnClickListener { authorizePairing() }
// QR Code
spinner.visibility = if (mode == Mode.Master) View.GONE else View.VISIBLE
qrCodeImageView.visibility = if (mode == Mode.Master) View.VISIBLE else View.GONE
if (mode == Mode.Master) {
val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
val displayMetrics = DisplayMetrics()
ServiceUtil.getWindowManager(context).defaultDisplay.getMetrics(displayMetrics)
val size = displayMetrics.widthPixels - 2 * toPx(96, resources)
val qrCode = QrCode.create(hexEncodedPublicKey, size)
qrCodeImageView.setImageBitmap(qrCode)
}
cancelButton.setOnClickListener { cancel() }
}
// endregion
// region Device Linking
fun requestUserAuthorization(pairingAuthorisation: PairingAuthorisation) {
if (mode != Mode.Master || pairingAuthorisation.type != PairingAuthorisation.Type.REQUEST || this.pairingAuthorisation != null) { return }
this.pairingAuthorisation = pairingAuthorisation
spinner.visibility = View.GONE
qrCodeImageView.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
mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), pairingAuthorisation.secondaryDevicePublicKey)
authorizeButton.visibility = View.VISIBLE
}
fun onDeviceLinkAuthorized(pairingAuthorisation: PairingAuthorisation) {
if (mode != Mode.Slave || pairingAuthorisation.type != PairingAuthorisation.Type.GRANT || this.pairingAuthorisation != null) { return }
this.pairingAuthorisation = pairingAuthorisation
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
cancelButton.visibility = View.GONE
Handler().postDelayed({
delegate.handleDeviceLinkAuthorized(pairingAuthorisation)
dismiss?.invoke()
}, 4000)
}
// endregion
// region Interaction
private fun authorizePairing() {
val pairingAuthorisation = this.pairingAuthorisation
if (mode != Mode.Master || pairingAuthorisation == null) { return; }
delegate.sendPairingAuthorizedMessage(pairingAuthorisation)
delegate.handleDeviceLinkAuthorized(pairingAuthorisation)
dismiss?.invoke()
}
private fun cancel() {
delegate.handleDeviceLinkingDialogDismissed()
dismiss?.invoke()
}
// endregion
}