From 3e8701d10fe15cd2bc712225dbd0ad6a9a7863a5 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 16 Jun 2024 23:12:29 +0930 Subject: [PATCH] Start loading account while user chooses notifications --- .../securesms/ApplicationContext.java | 2 +- .../securesms/home/HomeActivity.kt | 8 + .../securesms/onboarding/LandingActivity.kt | 47 +++-- .../onboarding/LinkDeviceActivity.kt | 8 +- .../onboarding/loading/LoadingActivity.kt | 22 +-- .../onboarding/loading/LoadingManager.kt | 59 ++++++ .../onboarding/loading/LoadingViewModel.kt | 88 +++------ .../MessageNotificationsActivity.kt | 20 +- .../pickname/PickDisplayNameActivity.kt | 19 +- .../pickname/PickDisplayNameViewModel.kt | 1 - .../recoverypassword/RecoveryPassword.kt | 185 ++++++++++++++++++ .../RecoveryPasswordActivity.kt | 181 ----------------- .../ui/color/ColorsFromPreferences.kt | 8 +- 13 files changed, 352 insertions(+), 296 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingManager.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/onboarding/recoverypassword/RecoveryPassword.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 3708915700..2e21dacd54 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -264,7 +264,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO // If the user account hasn't been created or onboarding wasn't finished then don't start // the pollers - if (TextSecurePreferences.getLocalNumber(this) == null || !TextSecurePreferences.hasSeenWelcomeScreen(this)) { + if (textSecurePreferences.getLocalNumber() == null || !textSecurePreferences.hasSeenWelcomeScreen()) { return; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt index ffa47c1860..cc63d2d245 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -4,6 +4,7 @@ import android.Manifest import android.app.NotificationManager import android.content.ClipData import android.content.ClipboardManager +import android.content.Context import android.content.Intent import android.os.Build import android.os.Bundle @@ -789,3 +790,10 @@ private fun EmptyView(newAccount: Boolean) { Spacer(modifier = Modifier.weight(2f)) } } + +fun Context.startHomeActivity() { + Intent(this, HomeActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + putExtra(HomeActivity.FROM_ONBOARDING, true) + }.also(::startActivity) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/LandingActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/LandingActivity.kt index df0d0cdbda..0cefbfcd2c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/LandingActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/LandingActivity.kt @@ -37,6 +37,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.delay import network.loki.messenger.R import org.session.libsession.utilities.TextSecurePreferences @@ -46,10 +47,9 @@ import org.thoughtcrime.securesms.onboarding.pickname.startPickDisplayNameActivi import org.thoughtcrime.securesms.service.KeyCachingService import org.thoughtcrime.securesms.showOpenUrlDialog import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.color.LocalColors -import org.thoughtcrime.securesms.ui.color.Colors import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.color.Colors import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.components.BorderlessHtmlButton import org.thoughtcrime.securesms.ui.components.PrimaryFillButton @@ -60,21 +60,13 @@ import org.thoughtcrime.securesms.ui.large import org.thoughtcrime.securesms.ui.setComposeContent import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo import org.thoughtcrime.securesms.util.start +import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds -private data class TextData( - @StringRes val stringId: Int, - val isOutgoing: Boolean = false -) +@AndroidEntryPoint +class LandingActivity: BaseActionBarActivity() { -private val MESSAGES = listOf( - TextData(R.string.onboardingBubbleWelcomeToSession), - TextData(R.string.onboardingBubbleSessionIsEngineered, isOutgoing = true), - TextData(R.string.onboardingBubbleNoPhoneNumber), - TextData(R.string.onboardingBubbleCreatingAnAccountIsEasy, isOutgoing = true) -) - -class LandingActivity : BaseActionBarActivity() { + @Inject lateinit var prefs: TextSecurePreferences override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -85,7 +77,14 @@ class LandingActivity : BaseActionBarActivity() { setUpActionBarSessionLogo(true) - setComposeContent { LandingScreen() } + setComposeContent { + LandingScreen( + createAccount = { + prefs.setHasViewedSeed(false) + startPickDisplayNameActivity() + } + ) + } IdentityKeyUtil.generateIdentityKeyPair(this) TextSecurePreferences.setPasswordDisabled(this, true) @@ -99,12 +98,12 @@ class LandingActivity : BaseActionBarActivity() { @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors ) { PreviewTheme(colors) { - LandingScreen() + LandingScreen {} } } @Composable - private fun LandingScreen() { + private fun LandingScreen(createAccount: () -> Unit) { var count by remember { mutableStateOf(0) } val listState = rememberLazyListState() @@ -159,7 +158,7 @@ class LandingActivity : BaseActionBarActivity() { .fillMaxWidth() .align(Alignment.CenterHorizontally) .contentDescription(R.string.AccessibilityId_create_account_button), - onClick = ::startPickDisplayNameActivity + onClick = createAccount ) Spacer(modifier = Modifier.height(LocalDimensions.current.itemSpacingExtraSmall)) PrimaryOutlineButton( @@ -252,3 +251,15 @@ private fun MessageText( ) } } + +private data class TextData( + @StringRes val stringId: Int, + val isOutgoing: Boolean = false +) + +private val MESSAGES = listOf( + TextData(R.string.onboardingBubbleWelcomeToSession), + TextData(R.string.onboardingBubbleSessionIsEngineered, isOutgoing = true), + TextData(R.string.onboardingBubbleNoPhoneNumber), + TextData(R.string.onboardingBubbleCreatingAnAccountIsEasy, isOutgoing = true) +) diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceActivity.kt index bb02438c27..c7d3d6ea55 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceActivity.kt @@ -30,7 +30,7 @@ import kotlinx.coroutines.launch import network.loki.messenger.R import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.BaseActionBarActivity -import org.thoughtcrime.securesms.onboarding.loading.startLoadingActivity +import org.thoughtcrime.securesms.onboarding.loading.LoadingManager import org.thoughtcrime.securesms.onboarding.messagenotifications.startMessageNotificationsActivity import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.PreviewTheme @@ -52,8 +52,8 @@ private val TITLES = listOf(R.string.sessionRecoveryPassword, R.string.qrScan) @androidx.annotation.OptIn(ExperimentalGetImage::class) class LinkDeviceActivity : BaseActionBarActivity() { - @Inject - lateinit var prefs: TextSecurePreferences + @Inject lateinit var prefs: TextSecurePreferences + @Inject lateinit var loadingManager: LoadingManager val viewModel: LinkDeviceViewModel by viewModels() @@ -67,8 +67,8 @@ class LinkDeviceActivity : BaseActionBarActivity() { lifecycleScope.launch { viewModel.eventFlow.collect { + loadingManager.load(it.mnemonic) startMessageNotificationsActivity() - startLoadingActivity(it.mnemonic) finish() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingActivity.kt index ebff016bce..bc2841ee1f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingActivity.kt @@ -1,6 +1,5 @@ package org.thoughtcrime.securesms.onboarding.loading -import android.content.Context import android.content.Intent import android.os.Bundle import androidx.activity.viewModels @@ -13,14 +12,12 @@ import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.dependencies.ConfigFactory -import org.thoughtcrime.securesms.onboarding.messagenotifications.startMessageNotificationsActivity +import org.thoughtcrime.securesms.home.startHomeActivity import org.thoughtcrime.securesms.onboarding.pickname.startPickDisplayNameActivity import org.thoughtcrime.securesms.ui.setComposeContent import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo import javax.inject.Inject -private const val EXTRA_MNEMONIC = "mnemonic" - @AndroidEntryPoint class LoadingActivity: BaseActionBarActivity() { @@ -40,11 +37,12 @@ class LoadingActivity: BaseActionBarActivity() { private fun register(skipped: Boolean) { prefs.setLastConfigurationSyncTime(System.currentTimeMillis()) - val flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - when { - skipped -> startPickDisplayNameActivity(true, flags) - else -> startMessageNotificationsActivity(flags) + skipped -> startPickDisplayNameActivity( + failedToLoad = true, + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + ) + else -> startHomeActivity() } } @@ -60,8 +58,6 @@ class LoadingActivity: BaseActionBarActivity() { setUpActionBarSessionLogo(true) - viewModel.restore(application, intent.getByteArrayExtra(EXTRA_MNEMONIC)!!) - lifecycleScope.launch { viewModel.eventFlow.collect { when (it) { @@ -72,9 +68,3 @@ class LoadingActivity: BaseActionBarActivity() { } } } - -fun Context.startLoadingActivity(mnemonic: ByteArray) { - Intent(this, LoadingActivity::class.java) - .apply { putExtra(EXTRA_MNEMONIC, mnemonic) } - .also(::startActivity) -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingManager.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingManager.kt new file mode 100644 index 0000000000..f12fa10468 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingManager.kt @@ -0,0 +1,59 @@ +package org.thoughtcrime.securesms.onboarding.loading + +import android.content.Context +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import org.session.libsession.snode.SnodeModule +import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsignal.database.LokiAPIDatabaseProtocol +import org.session.libsignal.utilities.hexEncodedPublicKey +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.dependencies.ConfigFactory +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class LoadingManager @Inject constructor( + @dagger.hilt.android.qualifiers.ApplicationContext val context: Context, + val configFactory: ConfigFactory, + val prefs: TextSecurePreferences +) { + val isLoading: Boolean get() = restoreJob?.isActive == true + + private val database: LokiAPIDatabaseProtocol + get() = SnodeModule.shared.storage + + private var restoreJob: Job? = null + + private val scope = CoroutineScope(Dispatchers.IO) + + fun load(seed: ByteArray) { + // only have one sync job running at a time (prevent QR from trying to spawn a new job) + if (restoreJob?.isActive == true) return + + restoreJob = scope.launch { + // 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() + + // RestoreActivity handles seed this way + val keyPairGenerationResult = org.thoughtcrime.securesms.crypto.KeyPairUtilities.generate(seed) + val x25519KeyPair = keyPairGenerationResult.x25519KeyPair + org.thoughtcrime.securesms.crypto.KeyPairUtilities.store(context, seed, keyPairGenerationResult.ed25519KeyPair, x25519KeyPair) + configFactory.keyPairChanged() + val userHexEncodedPublicKey = x25519KeyPair.hexEncodedPublicKey + val registrationID = org.session.libsignal.utilities.KeyHelper.generateRegistrationId(false) + prefs.apply { + setLocalRegistrationId(registrationID) + setLocalNumber(userHexEncodedPublicKey) + setRestorationTime(System.currentTimeMillis()) + setHasViewedSeed(true) + } + + ApplicationContext.getInstance(context).apply { startPollingIfNeeded() } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingViewModel.kt index 5bf54de634..f9834de0a7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingViewModel.kt @@ -1,26 +1,21 @@ package org.thoughtcrime.securesms.onboarding.loading -import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.flow.timeout import kotlinx.coroutines.launch -import org.session.libsession.snode.SnodeModule import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsignal.database.LokiAPIDatabaseProtocol -import org.session.libsignal.utilities.KeyHelper -import org.session.libsignal.utilities.hexEncodedPublicKey -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.crypto.KeyPairUtilities -import org.thoughtcrime.securesms.dependencies.ConfigFactory import javax.inject.Inject import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds @@ -31,67 +26,44 @@ data class State(val duration: Duration) private val ANIMATE_TO_DONE_TIME = 500.milliseconds private val IDLE_DONE_TIME = 1.seconds private val TIMEOUT_TIME = 15.seconds -private val TOTAL_ANIMATION_TIME = TIMEOUT_TIME - IDLE_DONE_TIME +@OptIn(FlowPreview::class) @HiltViewModel class LoadingViewModel @Inject constructor( - private val configFactory: ConfigFactory, - private val prefs: TextSecurePreferences, -) : ViewModel() { + val prefs: TextSecurePreferences +): ViewModel() { - private val state = MutableStateFlow(State(TOTAL_ANIMATION_TIME)) + private val state = MutableStateFlow(State(TIMEOUT_TIME)) val stateFlow = state.asStateFlow() private val event = Channel() val eventFlow = event.receiveAsFlow() - private var restoreJob: Job? = null - - internal val database: LokiAPIDatabaseProtocol - get() = SnodeModule.shared.storage - - fun restore(context: Context, seed: ByteArray) { - - // only have one sync job running at a time (prevent QR from trying to spawn a new job) - if (restoreJob?.isActive == true) return - - restoreJob = viewModelScope.launch(Dispatchers.IO) { - // 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() - - // RestoreActivity handles seed this way - val keyPairGenerationResult = KeyPairUtilities.generate(seed) - val x25519KeyPair = keyPairGenerationResult.x25519KeyPair - KeyPairUtilities.store(context, seed, keyPairGenerationResult.ed25519KeyPair, x25519KeyPair) - configFactory.keyPairChanged() - val userHexEncodedPublicKey = x25519KeyPair.hexEncodedPublicKey - val registrationID = KeyHelper.generateRegistrationId(false) - prefs.apply { - setLocalRegistrationId(registrationID) - setLocalNumber(userHexEncodedPublicKey) - setRestorationTime(System.currentTimeMillis()) - setHasViewedSeed(true) - } - - val skipJob = launch(Dispatchers.IO) { - delay(TIMEOUT_TIME) - event.send(Event.TIMEOUT) - } - - // start polling and wait for updated message - ApplicationContext.getInstance(context).apply { startPollingIfNeeded() } - TextSecurePreferences.events.filter { it == TextSecurePreferences.CONFIGURATION_SYNCED }.collect { - // handle we've synced - skipJob.cancel() - - state.value = State(ANIMATE_TO_DONE_TIME) - delay(IDLE_DONE_TIME) - event.send(Event.SUCCESS) + init { + viewModelScope.launch(Dispatchers.IO) { + try { + TextSecurePreferences.events + .filter { it == TextSecurePreferences.CONFIGURATION_SYNCED } + .timeout(TIMEOUT_TIME) + .onStart { emit(TextSecurePreferences.CONFIGURATION_SYNCED) } + .collectLatest { + if (prefs.getConfigurationMessageSynced()) onSuccess() + } + } catch (e: Exception) { + onFail() } } } + + private suspend fun onSuccess() { + state.value = State(ANIMATE_TO_DONE_TIME) + delay(IDLE_DONE_TIME) + event.send(Event.SUCCESS) + } + + private fun onFail() { + event.trySend(Event.TIMEOUT) + } } sealed interface Event { diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt index 998e38f4d5..a0829a572c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt @@ -12,22 +12,28 @@ import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.home.HomeActivity +import org.thoughtcrime.securesms.home.startHomeActivity import org.thoughtcrime.securesms.notifications.PushRegistry +import org.thoughtcrime.securesms.onboarding.loading.LoadingActivity +import org.thoughtcrime.securesms.onboarding.loading.LoadingManager import org.thoughtcrime.securesms.ui.setComposeContent import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo +import org.thoughtcrime.securesms.util.start import javax.inject.Inject @AndroidEntryPoint class MessageNotificationsActivity : BaseActionBarActivity() { @Inject lateinit var pushRegistry: PushRegistry + @Inject lateinit var prefs: TextSecurePreferences + @Inject lateinit var loadingManager: LoadingManager private val viewModel: MessageNotificationsViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setUpActionBarSessionLogo(true) - TextSecurePreferences.setHasSeenWelcomeScreen(this, true) + prefs.setHasSeenWelcomeScreen(true) setComposeContent { MessageNotificationsScreen() } } @@ -39,13 +45,15 @@ class MessageNotificationsActivity : BaseActionBarActivity() { } private fun register() { - TextSecurePreferences.setPushEnabled(this, viewModel.stateFlow.value.pushEnabled) + prefs.setPushEnabled(viewModel.stateFlow.value.pushEnabled) ApplicationContext.getInstance(this).startPollingIfNeeded() pushRegistry.refresh(true) - Intent(this, HomeActivity::class.java).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - putExtra(HomeActivity.FROM_ONBOARDING, true) - }.also(::startActivity) + + when { + prefs.getHasViewedSeed() && !prefs.getConfigurationMessageSynced() -> start() + prefs.getProfileName() != null -> startHomeActivity() + else -> startHomeActivity() + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameActivity.kt index 11ecfe47d0..8dc6ae48d1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameActivity.kt @@ -22,8 +22,10 @@ import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import network.loki.messenger.R +import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.BaseActionBarActivity +import org.thoughtcrime.securesms.home.startHomeActivity import org.thoughtcrime.securesms.onboarding.messagenotifications.startMessageNotificationsActivity import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.base @@ -35,17 +37,18 @@ import org.thoughtcrime.securesms.ui.setComposeContent import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo import javax.inject.Inject -private const val EXTRA_PICK_NEW_NAME = "extra_pick_new_name" +private const val EXTRA_FAILED_TO_LOAD = "extra_failed_to_load" @AndroidEntryPoint class PickDisplayNameActivity : BaseActionBarActivity() { - @Inject - lateinit var viewModelFactory: PickDisplayNameViewModel.AssistedFactory + @Inject lateinit var viewModelFactory: PickDisplayNameViewModel.AssistedFactory + @Inject lateinit var prefs: TextSecurePreferences + + val failedToLoad get() = intent.getBooleanExtra(EXTRA_FAILED_TO_LOAD, false) private val viewModel: PickDisplayNameViewModel by viewModels { - val pickNewName = intent.getBooleanExtra(EXTRA_PICK_NEW_NAME, false) - viewModelFactory.create(pickNewName) + viewModelFactory.create(failedToLoad) } override fun onCreate(savedInstanceState: Bundle?) { @@ -54,9 +57,11 @@ class PickDisplayNameActivity : BaseActionBarActivity() { setComposeContent { DisplayNameScreen(viewModel) } + if (!failedToLoad) prefs.setHasViewedSeed(false) + lifecycleScope.launch { viewModel.eventFlow.collect { - startMessageNotificationsActivity() + if (failedToLoad) startHomeActivity() else startMessageNotificationsActivity() } } } @@ -119,7 +124,7 @@ fun Context.startPickDisplayNameActivity(failedToLoad: Boolean = false, flags: I ApplicationContext.getInstance(this).newAccount = !failedToLoad Intent(this, PickDisplayNameActivity::class.java) - .apply { putExtra(EXTRA_PICK_NEW_NAME, failedToLoad) } + .apply { putExtra(EXTRA_FAILED_TO_LOAD, failedToLoad) } .also { it.flags = flags } .also(::startActivity) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameViewModel.kt index 62f64fe884..3dff6e39e7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameViewModel.kt @@ -65,7 +65,6 @@ class PickDisplayNameViewModel( prefs.setLocalRegistrationId(registrationID) prefs.setLocalNumber(userHexEncodedPublicKey) prefs.setRestorationTime(0) - prefs.setHasViewedSeed(false) viewModelScope.launch { event.send(Event.DONE) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/recoverypassword/RecoveryPassword.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/recoverypassword/RecoveryPassword.kt new file mode 100644 index 0000000000..d17ae68b11 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/recoverypassword/RecoveryPassword.kt @@ -0,0 +1,185 @@ +package org.thoughtcrime.securesms.onboarding.recoverypassword + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import network.loki.messenger.R +import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin +import org.thoughtcrime.securesms.ui.LocalDimensions +import org.thoughtcrime.securesms.ui.PreviewTheme +import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.SessionShieldIcon +import org.thoughtcrime.securesms.ui.base +import org.thoughtcrime.securesms.ui.color.Colors +import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.components.QrImage +import org.thoughtcrime.securesms.ui.components.SlimOutlineButton +import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton +import org.thoughtcrime.securesms.ui.contentDescription +import org.thoughtcrime.securesms.ui.extraSmallMonospace +import org.thoughtcrime.securesms.ui.h8 + + +@Preview +@Composable +fun PreviewRecoveryPasswordScreen( + @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors +) { + PreviewTheme(colors) { + RecoveryPasswordScreen(seed = "Voyage urban toyed maverick peculiar tuxedo penguin tree grass building listen speak withdraw terminal plane") + } +} + +@Composable +fun RecoveryPasswordScreen( + seed: String = "", + copySeed:() -> Unit = {}, + onHide:() -> Unit = {} +) { + Column( + verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.marginExtraSmall), + modifier = Modifier + .contentDescription(R.string.AccessibilityId_recovery_password) + .verticalScroll(rememberScrollState()) + .padding(bottom = LocalDimensions.current.marginExtraSmall) + ) { + RecoveryPasswordCell(seed, copySeed) + HideRecoveryPasswordCell(onHide) + } +} + +@Composable +fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) { + var showQr by remember { + mutableStateOf(false) + } + + CellWithPaddingAndMargin { + Column { + Row { + Text( + stringResource(R.string.sessionRecoveryPassword), + style = h8 + ) + Spacer(Modifier.width(LocalDimensions.current.itemSpacingExtraSmall)) + SessionShieldIcon() + } + + Spacer(modifier = Modifier.height(LocalDimensions.current.marginTiny)) + + Text( + stringResource(R.string.recoveryPasswordDescription), + style = base + ) + + AnimatedVisibility(!showQr) { + RecoveryPassword(seed) + } + + AnimatedVisibility( + showQr, + modifier = Modifier.align(Alignment.CenterHorizontally) + ) { + QrImage( + seed, + modifier = Modifier + .padding(vertical = LocalDimensions.current.marginSmall) + .contentDescription(R.string.AccessibilityId_qr_code), + icon = R.drawable.session_shield + ) + } + + AnimatedVisibility(!showQr) { + Row(horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.marginMedium)) { + SlimOutlineCopyButton( + Modifier.weight(1f), + onClick = copySeed + ) + SlimOutlineButton( + stringResource(R.string.qrView), + Modifier.weight(1f), + ) { showQr = !showQr } + } + } + + AnimatedVisibility(showQr, modifier = Modifier.align(Alignment.CenterHorizontally)) { + SlimOutlineButton( + stringResource(R.string.recoveryPasswordView), + onClick = { showQr = !showQr } + ) + } + } + } +} + +@Composable +private fun RecoveryPassword(seed: String) { + Text( + seed, + modifier = Modifier + .contentDescription(R.string.AccessibilityId_recovery_password_container) + .padding(vertical = LocalDimensions.current.marginSmall) + .border( + width = 1.dp, + color = LocalColors.current.borders, + shape = RoundedCornerShape(11.dp) + ) + .padding(LocalDimensions.current.marginSmall), + textAlign = TextAlign.Center, + style = extraSmallMonospace, + color = LocalColors.current.run { if (isLight) text else primary }, + ) +} + +@Composable +private fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) { + CellWithPaddingAndMargin { + Row { + Column( + Modifier.weight(1f) + ) { + Text( + stringResource(R.string.recoveryPasswordHideRecoveryPassword), + style = h8 + ) + Text( + stringResource(R.string.recoveryPasswordHideRecoveryPasswordDescription), + style = base + ) + } + Spacer(modifier = Modifier.width(LocalDimensions.current.marginExtraExtraSmall)) + SlimOutlineButton( + text = stringResource(R.string.hide), + modifier = Modifier + .wrapContentWidth() + .align(Alignment.CenterVertically) + .contentDescription(R.string.AccessibilityId_hide_recovery_password_button), + color = LocalColors.current.danger, + onClick = onHide + ) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/recoverypassword/RecoveryPasswordActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/recoverypassword/RecoveryPasswordActivity.kt index d916d345c5..2d3be3178c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/recoverypassword/RecoveryPasswordActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/recoverypassword/RecoveryPasswordActivity.kt @@ -2,49 +2,9 @@ package org.thoughtcrime.securesms.onboarding.recoverypassword import android.os.Bundle import androidx.activity.viewModels -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentWidth -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp import network.loki.messenger.R import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.showSessionDialog -import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.color.LocalColors -import org.thoughtcrime.securesms.ui.color.Colors -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider -import org.thoughtcrime.securesms.ui.SessionShieldIcon -import org.thoughtcrime.securesms.ui.base -import org.thoughtcrime.securesms.ui.components.QrImage -import org.thoughtcrime.securesms.ui.components.SlimOutlineButton -import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton -import org.thoughtcrime.securesms.ui.contentDescription -import org.thoughtcrime.securesms.ui.extraSmallMonospace -import org.thoughtcrime.securesms.ui.h8 import org.thoughtcrime.securesms.ui.setComposeContent class RecoveryPasswordActivity : BaseActionBarActivity() { @@ -87,144 +47,3 @@ class RecoveryPasswordActivity : BaseActionBarActivity() { } } } - -@Preview -@Composable -fun PreviewRecoveryPasswordScreen( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors -) { - PreviewTheme(colors) { - RecoveryPasswordScreen(seed = "Voyage urban toyed maverick peculiar tuxedo penguin tree grass building listen speak withdraw terminal plane") - } -} - -@Composable -fun RecoveryPasswordScreen( - seed: String = "", - copySeed:() -> Unit = {}, - onHide:() -> Unit = {} -) { - Column( - verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.marginExtraSmall), - modifier = Modifier - .contentDescription(R.string.AccessibilityId_recovery_password) - .verticalScroll(rememberScrollState()) - .padding(bottom = LocalDimensions.current.marginExtraSmall) - ) { - RecoveryPasswordCell(seed, copySeed) - HideRecoveryPasswordCell(onHide) - } -} - -@Composable -fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) { - var showQr by remember { - mutableStateOf(false) - } - - CellWithPaddingAndMargin { - Column { - Row { - Text( - stringResource(R.string.sessionRecoveryPassword), - style = h8 - ) - Spacer(Modifier.width(LocalDimensions.current.itemSpacingExtraSmall)) - SessionShieldIcon() - } - - Spacer(modifier = Modifier.height(LocalDimensions.current.marginTiny)) - - Text( - stringResource(R.string.recoveryPasswordDescription), - style = base - ) - - AnimatedVisibility(!showQr) { - RecoveryPassword(seed) - } - - AnimatedVisibility( - showQr, - modifier = Modifier.align(Alignment.CenterHorizontally) - ) { - QrImage( - seed, - modifier = Modifier - .padding(vertical = LocalDimensions.current.marginSmall) - .contentDescription(R.string.AccessibilityId_qr_code), - icon = R.drawable.session_shield - ) - } - - AnimatedVisibility(!showQr) { - Row(horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.marginMedium)) { - SlimOutlineCopyButton( - Modifier.weight(1f), - onClick = copySeed - ) - SlimOutlineButton( - stringResource(R.string.qrView), - Modifier.weight(1f), - ) { showQr = !showQr } - } - } - - AnimatedVisibility(showQr, modifier = Modifier.align(Alignment.CenterHorizontally)) { - SlimOutlineButton( - stringResource(R.string.recoveryPasswordView), - onClick = { showQr = !showQr } - ) - } - } - } -} - -@Composable -private fun RecoveryPassword(seed: String) { - Text( - seed, - modifier = Modifier - .contentDescription(R.string.AccessibilityId_recovery_password_container) - .padding(vertical = LocalDimensions.current.marginSmall) - .border( - width = 1.dp, - color = LocalColors.current.borders, - shape = RoundedCornerShape(11.dp) - ) - .padding(LocalDimensions.current.marginSmall), - textAlign = TextAlign.Center, - style = extraSmallMonospace, - color = LocalColors.current.run { if (isLight) text else primary }, - ) -} - -@Composable -private fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) { - CellWithPaddingAndMargin { - Row { - Column( - Modifier.weight(1f) - ) { - Text( - stringResource(R.string.recoveryPasswordHideRecoveryPassword), - style = h8 - ) - Text( - stringResource(R.string.recoveryPasswordHideRecoveryPasswordDescription), - style = base - ) - } - Spacer(modifier = Modifier.width(LocalDimensions.current.marginExtraExtraSmall)) - SlimOutlineButton( - text = stringResource(R.string.hide), - modifier = Modifier - .wrapContentWidth() - .align(Alignment.CenterVertically) - .contentDescription(R.string.AccessibilityId_hide_recovery_password_button), - color = LocalColors.current.danger, - onClick = onHide - ) - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/color/ColorsFromPreferences.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/color/ColorsFromPreferences.kt index c774b5d0fa..a65c08cba0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/color/ColorsFromPreferences.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/color/ColorsFromPreferences.kt @@ -19,10 +19,10 @@ import org.session.libsession.utilities.TextSecurePreferences.Companion.YELLOW_A */ @Composable fun TextSecurePreferences.colors(): Colors = lightDarkColors().colors() -fun TextSecurePreferences.lightDarkColors() = LightDarkColors(isClassic(), isLight(), getFollowSystemSettings(), primaryColor()) -fun TextSecurePreferences.isLight(): Boolean = getThemeStyle() in setOf(CLASSIC_LIGHT, OCEAN_LIGHT) -fun TextSecurePreferences.isClassic(): Boolean = getThemeStyle() in setOf(CLASSIC_DARK, CLASSIC_LIGHT) -fun TextSecurePreferences.primaryColor(): Color = when(getSelectedAccentColor()) { +private fun TextSecurePreferences.lightDarkColors() = LightDarkColors(isClassic(), isLight(), getFollowSystemSettings(), primaryColor()) +private fun TextSecurePreferences.isLight(): Boolean = getThemeStyle() in setOf(CLASSIC_LIGHT, OCEAN_LIGHT) +private fun TextSecurePreferences.isClassic(): Boolean = getThemeStyle() in setOf(CLASSIC_DARK, CLASSIC_LIGHT) +private fun TextSecurePreferences.primaryColor(): Color = when(getSelectedAccentColor()) { GREEN_ACCENT -> primaryGreen BLUE_ACCENT -> primaryBlue PURPLE_ACCENT -> primaryPurple