From 1f8b9bfe5872fbd288a78a786efb831ff0b97ca6 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 26 Feb 2021 11:10:45 +1100 Subject: [PATCH 01/25] Implement multi device UI --- .../loki/activities/LandingActivity.kt | 25 +--- .../loki/activities/LinkDeviceActivity.kt | 125 ++++++++++++++++++ .../res/layout-sw400dp/activity_landing.xml | 13 +- .../fragment_recovery_phrase.xml | 63 +++++++++ app/src/main/res/layout/activity_landing.xml | 13 +- .../main/res/layout/activity_link_device.xml | 14 ++ .../res/layout/fragment_recovery_phrase.xml | 63 +++++++++ app/src/main/res/xml/preferences_chats.xml | 4 +- 8 files changed, 275 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt create mode 100644 app/src/main/res/layout-sw400dp/fragment_recovery_phrase.xml create mode 100644 app/src/main/res/layout/activity_link_device.xml create mode 100644 app/src/main/res/layout/fragment_recovery_phrase.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt index 5cdf7cde5..882671bd6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt @@ -23,20 +23,15 @@ class LandingActivity : BaseActionBarActivity() { setContentView(R.layout.activity_landing) setUpActionBarSessionLogo(true) findViewById(R.id.fakeChatView).startAnimating() - findViewById(R.id.registerButton).setOnClickListener { register() } - findViewById(R.id.restoreButton).setOnClickListener { restoreFromRecoveryPhrase() } - findViewById(R.id.restoreBackupButton).setOnClickListener { restoreFromBackup() } - - // Setup essentials for a new user. + findViewById(R.id.restoreButton).setOnClickListener { restore() } + findViewById(R.id.linkButton).setOnClickListener { link() } IdentityKeyUtil.generateIdentityKeyPair(this) - TextSecurePreferences.setLastExperienceVersionCode(this, Util.getCanonicalVersionCode()) TextSecurePreferences.setPasswordDisabled(this, true) TextSecurePreferences.setReadReceiptsEnabled(this, true) TextSecurePreferences.setTypingIndicatorsEnabled(this, true) - - //AC: This is a temporary workaround to trick the old code that the screen is unlocked. + // AC: This is a temporary workaround to trick the old code that the screen is unlocked. KeyCachingService.setMasterSecret(applicationContext, Object()) } @@ -45,21 +40,13 @@ class LandingActivity : BaseActionBarActivity() { push(intent) } - private fun restoreFromRecoveryPhrase() { + private fun restore() { val intent = Intent(this, RecoveryPhraseRestoreActivity::class.java) push(intent) } - private fun restoreFromBackup() { - val intent = Intent(this, BackupRestoreActivity::class.java) + private fun link() { + val intent = Intent(this, LinkDeviceActivity::class.java) push(intent) } - - private fun reset() { - IdentityKeyUtil.delete(this, IdentityKeyUtil.LOKI_SEED) - TextSecurePreferences.removeLocalNumber(this) - TextSecurePreferences.setHasSeenWelcomeScreen(this, false) - val application = ApplicationContext.getInstance(this) - application.stopPolling() - } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt new file mode 100644 index 000000000..c49e83e50 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt @@ -0,0 +1,125 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.content.Context +import android.os.Bundle +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentPagerAdapter +import android.text.InputType +import android.view.* +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputMethodManager +import android.widget.Toast +import kotlinx.android.synthetic.main.activity_create_private_chat.* +import kotlinx.android.synthetic.main.fragment_recovery_phrase.* +import network.loki.messenger.R +import org.session.libsignal.service.loki.crypto.MnemonicCodec +import org.session.libsignal.utilities.Hex +import org.thoughtcrime.securesms.BaseActionBarActivity +import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment +import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate +import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities +import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo + +class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDelegate { + private val adapter = LinkDeviceActivityAdapter(this) + + // region Lifecycle + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setUpActionBarSessionLogo() + setContentView(R.layout.activity_link_device) + viewPager.adapter = adapter + tabLayout.setupWithViewPager(viewPager) + } + // endregion + + // region Interaction + override fun handleQRCodeScanned(mnemonic: String) { + continueWithMnemonic(mnemonic) + } + + fun continueWithMnemonic(mnemonic: String) { + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(this, fileName) + } + try { + val hexEncodedSeed = MnemonicCodec(loadFileContents).decode(mnemonic) + val seed = Hex.fromStringCondensed(hexEncodedSeed) + continueWithSeed(seed) + } catch (error: Exception) { + val message = if (error is MnemonicCodec.DecodingError) { + error.description + } else { + "An error occurred." + } + Toast.makeText(this, message, Toast.LENGTH_LONG).show() + } + } + + private fun continueWithSeed(seed: ByteArray) { + // TODO: Implement + } + // endregion +} + +// region Adapter +private class LinkDeviceActivityAdapter(private val activity: LinkDeviceActivity) : FragmentPagerAdapter(activity.supportFragmentManager) { + val recoveryPhraseFragment = RecoveryPhraseFragment() + + override fun getCount(): Int { + return 2 + } + + override fun getItem(index: Int): Fragment { + return when (index) { + 0 -> recoveryPhraseFragment + 1 -> { + val result = ScanQRCodeWrapperFragment() + result.delegate = activity + result.message = "Navigate to Settings → Recovery Phrase on your other device to show your QR code." + result + } + else -> throw IllegalStateException() + } + } + + override fun getPageTitle(index: Int): CharSequence? { + return when (index) { + 0 -> "Recovery Phrase" + 1 -> "Scan QR Code" + else -> throw IllegalStateException() + } + } +} +// endregion + +// region Recovery Phrase Fragment +class RecoveryPhraseFragment : Fragment() { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return inflater.inflate(R.layout.fragment_recovery_phrase, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mnemonicEditText.imeOptions = EditorInfo.IME_ACTION_DONE or 16777216 // Always use incognito keyboard + mnemonicEditText.setRawInputType(InputType.TYPE_CLASS_TEXT) + mnemonicEditText.setOnEditorActionListener { v, actionID, _ -> + if (actionID == EditorInfo.IME_ACTION_DONE) { + val imm = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(v.windowToken, 0) + handleContinueButtonTapped() + true + } else { + false + } + } + continueButton.setOnClickListener { handleContinueButtonTapped() } + } + + private fun handleContinueButtonTapped() { + val mnemonic = mnemonicEditText.text?.trim().toString() + (requireActivity() as LinkDeviceActivity).continueWithMnemonic(mnemonic) + } +} +// endregion diff --git a/app/src/main/res/layout-sw400dp/activity_landing.xml b/app/src/main/res/layout-sw400dp/activity_landing.xml index 606789cf7..52906283a 100644 --- a/app/src/main/res/layout-sw400dp/activity_landing.xml +++ b/app/src/main/res/layout-sw400dp/activity_landing.xml @@ -49,27 +49,16 @@ android:layout_marginRight="@dimen/massive_spacing" android:text="@string/activity_landing_restore_button_title" /> -