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/redesign/activities/RegisterActivity.kt

169 lines
7.2 KiB
Kotlin

package org.thoughtcrime.securesms.loki.redesign.activities
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
5 years ago
import android.graphics.Typeface
import android.net.Uri
import android.os.Bundle
5 years ago
import android.os.Handler
5 years ago
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
5 years ago
import android.text.style.StyleSpan
import android.view.View
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_register.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.IdentityDatabase
import org.thoughtcrime.securesms.loki.redesign.utilities.push
5 years ago
import org.thoughtcrime.securesms.loki.redesign.utilities.setUpActionBarSessionLogo
import org.thoughtcrime.securesms.util.Base64
import org.thoughtcrime.securesms.util.Hex
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.curve25519.Curve25519
import org.whispersystems.libsignal.ecc.Curve
import org.whispersystems.libsignal.ecc.ECKeyPair
import org.whispersystems.libsignal.util.KeyHelper
import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey
import java.io.File
import java.io.FileOutputStream
class RegisterActivity : BaseActionBarActivity() {
private var seed: ByteArray? = null
private var keyPair: ECKeyPair? = null
set(value) { field = value; updatePublicKeyTextView() }
// region Lifecycle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_register)
setUpLanguageFileDirectory()
5 years ago
setUpActionBarSessionLogo()
registerButton.setOnClickListener { register() }
copyButton.setOnClickListener { copyPublicKey() }
val termsExplanation = SpannableStringBuilder("By using this service, you agree to our Terms of Service and Privacy Policy")
termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
termsExplanation.setSpan(object : ClickableSpan() {
override fun onClick(widget: View) {
openURL("https://getsession.org/legal/#tos")
}
}, 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
termsExplanation.setSpan(object : ClickableSpan() {
override fun onClick(widget: View) {
openURL("https://getsession.org/legal/#privacy-policy")
}
}, 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
termsTextView.movementMethod = LinkMovementMethod.getInstance()
termsTextView.text = termsExplanation
updateKeyPair()
}
// endregion
// region General
private fun setUpLanguageFileDirectory() {
val languages = listOf( "english", "japanese", "portuguese", "spanish" )
val directory = File(applicationInfo.dataDir)
for (language in languages) {
val fileName = "$language.txt"
if (directory.list().contains(fileName)) { continue }
val inputStream = 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()
}
}
// endregion
// region Updating
private fun updateKeyPair() {
val seedCandidate = Curve25519.getInstance(Curve25519.BEST).generateSeed(16)
try {
this.keyPair = Curve.generateKeyPair(seedCandidate + seedCandidate) // Validate the seed
} catch (exception: Exception) {
return updateKeyPair()
}
seed = seedCandidate
}
private fun updatePublicKeyTextView() {
5 years ago
val hexEncodedPublicKey = keyPair!!.hexEncodedPublicKey
val characterCount = hexEncodedPublicKey.count()
var count = 0
val limit = 32
5 years ago
fun animate() {
val numberOfIndexesToShuffle = 32 - count
5 years ago
val indexesToShuffle = (0 until characterCount).shuffled().subList(0, numberOfIndexesToShuffle)
var mangledHexEncodedPublicKey = hexEncodedPublicKey
for (index in indexesToShuffle) {
try {
mangledHexEncodedPublicKey = mangledHexEncodedPublicKey.substring(0, index) + "0123456789abcdef__".random() + mangledHexEncodedPublicKey.substring(index + 1, mangledHexEncodedPublicKey.count())
5 years ago
} catch (exception: Exception) {
// Do nothing
}
}
count += 1
if (count < limit) {
publicKeyTextView.text = mangledHexEncodedPublicKey
Handler().postDelayed({
animate()
}, 32)
5 years ago
} else {
publicKeyTextView.text = hexEncodedPublicKey
}
}
animate()
}
// endregion
// region Interaction
private fun register() {
IdentityKeyUtil.save(this, IdentityKeyUtil.lokiSeedKey, Hex.toStringCondensed(seed))
IdentityKeyUtil.save(this, IdentityKeyUtil.IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(keyPair!!.publicKey.serialize()))
IdentityKeyUtil.save(this, IdentityKeyUtil.IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(keyPair!!.privateKey.serialize()))
val userHexEncodedPublicKey = keyPair!!.hexEncodedPublicKey
val registrationID = KeyHelper.generateRegistrationId(false)
TextSecurePreferences.setLocalRegistrationId(this, registrationID)
DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(userHexEncodedPublicKey),
IdentityKeyUtil.getIdentityKeyPair(this).publicKey, IdentityDatabase.VerifiedStatus.VERIFIED,
true, System.currentTimeMillis(), true)
TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey)
5 years ago
TextSecurePreferences.setRestorationTime(this, 0)
TextSecurePreferences.setHasViewedSeed(this, false)
val intent = Intent(this, DisplayNameActivity::class.java)
push(intent)
}
private fun copyPublicKey() {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Session ID", keyPair!!.hexEncodedPublicKey)
clipboard.primaryClip = clip
Toast.makeText(this, R.string.activity_register_public_key_copied_message, Toast.LENGTH_SHORT).show()
}
private fun openURL(url: String) {
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
} catch (e: Exception) {
Toast.makeText(this, "Couldn't open link", Toast.LENGTH_SHORT).show()
}
}
// endregion
}