session-android/app/src/main/java/org/thoughtcrime/securesms/onboarding/RecoveryPhraseRestoreActivi...

114 lines
5.3 KiB
Kotlin

package org.thoughtcrime.securesms.onboarding
import android.content.Intent
import android.graphics.Typeface
import android.net.Uri
import android.os.Bundle
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.text.style.StyleSpan
import android.view.View
import android.widget.Toast
import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R
import network.loki.messenger.databinding.ActivityRecoveryPhraseRestoreBinding
import org.session.libsession.snode.SnodeModule
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.crypto.MnemonicCodec
import org.session.libsignal.database.LokiAPIDatabaseProtocol
import org.session.libsignal.utilities.Hex
import org.session.libsignal.utilities.KeyHelper
import org.session.libsignal.utilities.hexEncodedPublicKey
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.crypto.KeyPairUtilities
import org.thoughtcrime.securesms.crypto.MnemonicUtilities
import org.thoughtcrime.securesms.dependencies.ConfigFactory
import org.thoughtcrime.securesms.util.push
import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo
import javax.inject.Inject
@AndroidEntryPoint
class RecoveryPhraseRestoreActivity : BaseActionBarActivity() {
@Inject
lateinit var configFactory: ConfigFactory
private lateinit var binding: ActivityRecoveryPhraseRestoreBinding
internal val database: LokiAPIDatabaseProtocol
get() = SnodeModule.shared.storage
// region Lifecycle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setUpActionBarSessionLogo()
TextSecurePreferences.apply {
setHasViewedSeed(this@RecoveryPhraseRestoreActivity, true)
setConfigurationMessageSynced(this@RecoveryPhraseRestoreActivity, false)
setRestorationTime(this@RecoveryPhraseRestoreActivity, System.currentTimeMillis())
setLastProfileUpdateTime(this@RecoveryPhraseRestoreActivity, System.currentTimeMillis())
}
binding = ActivityRecoveryPhraseRestoreBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.mnemonicEditText.imeOptions = binding.mnemonicEditText.imeOptions or 16777216 // Always use incognito keyboard
binding.restoreButton.setOnClickListener { restore() }
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/terms-of-service/")
}
}, 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/privacy-policy/")
}
}, 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
binding.termsTextView.movementMethod = LinkMovementMethod.getInstance()
binding.termsTextView.text = termsExplanation
}
// endregion
// region Interaction
private fun restore() {
val mnemonic = binding.mnemonicEditText.text.toString()
try {
// This is here to resolve a case where the app restarts before a user completes onboarding
// which can result in an invalid database state
database.clearAllLastMessageHashes()
database.clearReceivedMessageHashValues()
val loadFileContents: (String) -> String = { fileName ->
MnemonicUtilities.loadFileContents(this, fileName)
}
val hexEncodedSeed = MnemonicCodec(loadFileContents).decode(mnemonic)
val seed = Hex.fromStringCondensed(hexEncodedSeed)
val keyPairGenerationResult = KeyPairUtilities.generate(seed)
val x25519KeyPair = keyPairGenerationResult.x25519KeyPair
KeyPairUtilities.store(this, seed, keyPairGenerationResult.ed25519KeyPair, x25519KeyPair)
configFactory.keyPairChanged()
val userHexEncodedPublicKey = x25519KeyPair.hexEncodedPublicKey
val registrationID = KeyHelper.generateRegistrationId(false)
TextSecurePreferences.setLocalRegistrationId(this, registrationID)
TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey)
val intent = Intent(this, DisplayNameActivity::class.java)
push(intent)
} catch (e: Exception) {
val message = if (e is MnemonicCodec.DecodingError) e.description else MnemonicCodec.DecodingError.Generic.description
return Toast.makeText(this, 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, R.string.invalid_url, Toast.LENGTH_SHORT).show()
}
}
// endregion
}