mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-07-02 03:56:23 +01:00
Add verification code requested alert handling.
This commit is contained in:
committed by
jeffrey-signal
parent
6722a28f98
commit
3b93edcdaf
@@ -116,6 +116,7 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.GooglePay
|
||||
import org.thoughtcrime.securesms.components.snackbars.LocalSnackbarStateConsumerRegistry
|
||||
import org.thoughtcrime.securesms.components.snackbars.SnackbarHostKey
|
||||
import org.thoughtcrime.securesms.components.snackbars.SnackbarState
|
||||
import org.thoughtcrime.securesms.components.verificationrequested.VerificationCodeRequestedBottomSheet
|
||||
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController
|
||||
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner
|
||||
import org.thoughtcrime.securesms.conversation.ConversationIntents
|
||||
@@ -195,6 +196,7 @@ import org.thoughtcrime.securesms.window.AppScaffoldNavigator
|
||||
import org.thoughtcrime.securesms.window.NavigationType
|
||||
import org.thoughtcrime.securesms.window.rememberThreePaneScaffoldNavigatorDelegate
|
||||
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
import org.signal.core.ui.R as CoreUiR
|
||||
|
||||
class MainActivity :
|
||||
@@ -357,6 +359,25 @@ class MainActivity :
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
SignalStore
|
||||
.account
|
||||
.verificationCodeRequestedAtMsFlow
|
||||
.filter { it > 0L }
|
||||
.collect { requestedAt ->
|
||||
val notificationThreshold = requestedAt + 10.minutes.inWholeMilliseconds
|
||||
if (System.currentTimeMillis() < notificationThreshold) {
|
||||
VerificationCodeRequestedBottomSheet.show(supportFragmentManager, requestedAt)
|
||||
} else {
|
||||
Log.i(TAG, "Verification code requested but is older than 10 minutes, not showing sheet")
|
||||
}
|
||||
|
||||
SignalStore.account.verificationCodeRequestedAtMs = 0L
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
supportFragmentManager.setFragmentResultListener(
|
||||
|
||||
+4
@@ -86,6 +86,7 @@ class AppSettingsActivity : DSLSettingsActivity(), GooglePayComponent {
|
||||
is AppSettingsRoute.BackupsRoute.Backups -> AppSettingsFragmentDirections.actionDirectToBackupsSettingsFragment().setLaunchCheckoutFlow(appSettingsRoute.launchCheckoutFlow)
|
||||
AppSettingsRoute.Invite -> AppSettingsFragmentDirections.actionDirectToInviteFragment()
|
||||
AppSettingsRoute.DataAndStorageRoute.DataAndStorage -> AppSettingsFragmentDirections.actionDirectToStoragePreferenceFragment()
|
||||
AppSettingsRoute.AccountRoute.Account -> AppSettingsFragmentDirections.actionDirectToAccountSettingsFragment()
|
||||
else -> error("Unsupported start location: ${appSettingsRoute?.javaClass?.name}")
|
||||
}
|
||||
}
|
||||
@@ -177,6 +178,9 @@ class AppSettingsActivity : DSLSettingsActivity(), GooglePayComponent {
|
||||
@JvmStatic
|
||||
fun changeNumber(context: Context): Intent = getIntentForStartLocation(context, AppSettingsRoute.ChangeNumberRoute.Start)
|
||||
|
||||
@JvmStatic
|
||||
fun account(context: Context): Intent = getIntentForStartLocation(context, AppSettingsRoute.AccountRoute.Account)
|
||||
|
||||
@JvmStatic
|
||||
fun subscriptions(context: Context): Intent = getIntentForStartLocation(context, AppSettingsRoute.DonationsRoute.Donations(directToCheckoutType = InAppPaymentType.RECURRING_DONATION))
|
||||
|
||||
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.verificationrequested
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalResources
|
||||
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import org.signal.core.ui.BottomSheetUtil
|
||||
import org.signal.core.ui.compose.BottomSheets
|
||||
import org.signal.core.ui.compose.Buttons
|
||||
import org.signal.core.ui.compose.ComposeBottomSheetDialogFragment
|
||||
import org.signal.core.ui.compose.DayNightPreviews
|
||||
import org.signal.core.ui.compose.Previews
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.util.DateUtils
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Sheet shown when the server has pushed a notification telling us a verification code was
|
||||
* requested for the user's account.
|
||||
*/
|
||||
class VerificationCodeRequestedBottomSheet : ComposeBottomSheetDialogFragment() {
|
||||
|
||||
override val peekHeightPercentage: Float = 1f
|
||||
|
||||
companion object {
|
||||
private const val ARG_REQUESTED_AT = "requested_at"
|
||||
|
||||
@JvmStatic
|
||||
fun show(fragmentManager: FragmentManager, requestedAtMs: Long) {
|
||||
VerificationCodeRequestedBottomSheet().apply {
|
||||
arguments = Bundle().apply { putLong(ARG_REQUESTED_AT, requestedAtMs) }
|
||||
}.show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun SheetContent() {
|
||||
val context = LocalContext.current
|
||||
val resources = LocalResources.current
|
||||
val requestedAt = requireArguments().getLong(ARG_REQUESTED_AT)
|
||||
val formattedTime = remember(requestedAt) {
|
||||
val time = DateUtils.getOnlyTimeString(context, requestedAt)
|
||||
val day = DateUtils.getDayPrecisionTimeString(context, Locale.getDefault(), requestedAt)
|
||||
resources.getString(R.string.VerificationCodeRequestedBottomSheet__time_with_day, time, day)
|
||||
}
|
||||
val nestedScrollInterop = rememberNestedScrollInteropConnection()
|
||||
val scrollModifier = Modifier.nestedScroll(nestedScrollInterop)
|
||||
|
||||
VerificationCodeRequestedContent(
|
||||
formattedTime = formattedTime,
|
||||
onSafetyTipsClicked = {
|
||||
val fragmentManager = parentFragmentManager
|
||||
dismissAllowingStateLoss()
|
||||
VerificationCodeRequestedSafetyTipsBottomSheet.show(fragmentManager)
|
||||
},
|
||||
onOkClicked = { dismissAllowingStateLoss() },
|
||||
modifier = scrollModifier
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun VerificationCodeRequestedContent(
|
||||
formattedTime: String,
|
||||
onSafetyTipsClicked: () -> Unit,
|
||||
onOkClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
BottomSheets.Handle()
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier
|
||||
.weight(weight = 1f, fill = false)
|
||||
.verticalScroll(state = scrollState)
|
||||
.padding(horizontal = 36.dp)
|
||||
.padding(bottom = 36.dp)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(26.dp))
|
||||
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.verificationcode_alert_96),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(96.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.VerificationCodeRequestedBottomSheet__title),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
Text(
|
||||
text = formattedTime,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.VerificationCodeRequestedBottomSheet__body_1),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.VerificationCodeRequestedBottomSheet__body_2),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
Buttons.LargeTonal(
|
||||
onClick = onSafetyTipsClicked,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp)
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.VerificationCodeRequestedBottomSheet__safety_tips))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Buttons.LargeTonal(
|
||||
onClick = onOkClicked,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp)
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.VerificationCodeRequestedBottomSheet__ok))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
private fun VerificationCodeRequestedContentPreview() {
|
||||
Previews.BottomSheetContentPreview {
|
||||
VerificationCodeRequestedContent(
|
||||
formattedTime = "3:25 PM Today",
|
||||
onSafetyTipsClicked = {},
|
||||
onOkClicked = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.verificationrequested
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import org.signal.core.ui.BottomSheetUtil
|
||||
import org.signal.core.ui.compose.BottomSheets
|
||||
import org.signal.core.ui.compose.Buttons
|
||||
import org.signal.core.ui.compose.ComposeBottomSheetDialogFragment
|
||||
import org.signal.core.ui.compose.DayNightPreviews
|
||||
import org.signal.core.ui.compose.Previews
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
|
||||
|
||||
/**
|
||||
* Sheet showing safety tips related to a verification code alert.
|
||||
*/
|
||||
class VerificationCodeRequestedSafetyTipsBottomSheet : ComposeBottomSheetDialogFragment() {
|
||||
|
||||
override val peekHeightPercentage: Float = 1f
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun show(fragmentManager: FragmentManager) {
|
||||
VerificationCodeRequestedSafetyTipsBottomSheet().show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun SheetContent() {
|
||||
val nestedScrollInterop = rememberNestedScrollInteropConnection()
|
||||
val scrollModifier = Modifier.nestedScroll(nestedScrollInterop)
|
||||
|
||||
SafetyTipsContent(
|
||||
onOpenAccountSettings = {
|
||||
startActivity(AppSettingsActivity.account(requireContext()))
|
||||
dismissAllowingStateLoss()
|
||||
},
|
||||
modifier = scrollModifier
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SafetyTipsContent(
|
||||
onOpenAccountSettings: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
BottomSheets.Handle()
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier
|
||||
.weight(weight = 1f, fill = false)
|
||||
.verticalScroll(state = scrollState)
|
||||
.padding(horizontal = 36.dp)
|
||||
.padding(bottom = 36.dp)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(26.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.SafetyTipsBottomSheet__title),
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(36.dp))
|
||||
|
||||
SafetyTipRow(
|
||||
iconRes = R.drawable.safetytip_48_message,
|
||||
titleRes = R.string.SafetyTipsBottomSheet__tip_1_title,
|
||||
bodyRes = R.string.SafetyTipsBottomSheet__tip_1_body
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
|
||||
SafetyTipRow(
|
||||
iconRes = R.drawable.safetytip_48_pin,
|
||||
titleRes = R.string.SafetyTipsBottomSheet__tip_2_title,
|
||||
bodyRes = R.string.SafetyTipsBottomSheet__tip_2_body
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
|
||||
SafetyTipRow(
|
||||
iconRes = R.drawable.safetytip_48_lock,
|
||||
titleRes = R.string.SafetyTipsBottomSheet__tip_3_title,
|
||||
bodyRes = R.string.SafetyTipsBottomSheet__tip_3_body
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
|
||||
Buttons.LargeTonal(
|
||||
onClick = onOpenAccountSettings,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp)
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.SafetyTipsBottomSheet__open_account_settings))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SafetyTipRow(
|
||||
iconRes: Int,
|
||||
titleRes: Int,
|
||||
bodyRes: Int
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(24.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = iconRes),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(48.dp)
|
||||
)
|
||||
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
text = stringResource(id = titleRes),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = bodyRes),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
private fun SafetyTipsContentPreview() {
|
||||
Previews.BottomSheetContentPreview {
|
||||
SafetyTipsContent(onOpenAccountSettings = {})
|
||||
}
|
||||
}
|
||||
@@ -35,13 +35,16 @@ public class FcmReceiveService extends FirebaseMessagingService {
|
||||
remoteMessage.getOriginalPriority(),
|
||||
NetworkUtil.getNetworkStatus(this)));
|
||||
|
||||
String registrationChallenge = remoteMessage.getData().get("challenge");
|
||||
String rateLimitChallenge = remoteMessage.getData().get("rateLimitChallenge");
|
||||
String registrationChallenge = remoteMessage.getData().get("challenge");
|
||||
String rateLimitChallenge = remoteMessage.getData().get("rateLimitChallenge");
|
||||
String verificationCodeRequest = remoteMessage.getData().get("verificationCodeRequested");
|
||||
|
||||
if (registrationChallenge != null) {
|
||||
handleRegistrationPushChallenge(registrationChallenge);
|
||||
} else if (rateLimitChallenge != null) {
|
||||
handleRateLimitPushChallenge(rateLimitChallenge);
|
||||
} else if (verificationCodeRequest != null && SignalStore.account().isPrimaryDevice()) {
|
||||
handleVerificationCodeRequested(verificationCodeRequest, remoteMessage.getSentTime());
|
||||
} else {
|
||||
handleReceivedNotification(AppDependencies.getApplication(), remoteMessage);
|
||||
}
|
||||
@@ -102,4 +105,20 @@ public class FcmReceiveService extends FirebaseMessagingService {
|
||||
Log.d(TAG, "Got a rate limit push challenge.");
|
||||
AppDependencies.getJobManager().add(new SubmitRateLimitPushChallengeJob(challenge));
|
||||
}
|
||||
|
||||
private static void handleVerificationCodeRequested(String verificationCodeRequestJson, long sentTime) {
|
||||
Log.i(TAG, "Got a verification code requested push.");
|
||||
|
||||
VerificationCodeRequestedPush verificationRequestedPush = VerificationCodeRequestedPush.fromJson(verificationCodeRequestJson);
|
||||
|
||||
long requestedAt;
|
||||
if (verificationRequestedPush != null && verificationRequestedPush.getTimestamp() != null) {
|
||||
requestedAt = verificationRequestedPush.getTimestamp();
|
||||
} else {
|
||||
Log.w(TAG, "Unable to parse requested at timestamp from server, using sent time instead");
|
||||
requestedAt = sentTime;
|
||||
}
|
||||
|
||||
SignalStore.account().setVerificationCodeRequestedAtMs(requestedAt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.gcm
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.signal.core.util.logging.Log
|
||||
|
||||
@Serializable
|
||||
data class VerificationCodeRequestedPush(val timestamp: Long?) {
|
||||
companion object {
|
||||
|
||||
private val TAG = Log.tag(VerificationCodeRequestedPush::class)
|
||||
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
@JvmStatic
|
||||
fun fromJson(jsonString: String): VerificationCodeRequestedPush? {
|
||||
return try {
|
||||
json.decodeFromString(jsonString)
|
||||
} catch (e: Throwable) {
|
||||
Log.w(TAG, "Unable to parse verification code request", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.preference.PreferenceManager
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.signal.core.models.AccountEntropyPool
|
||||
import org.signal.core.models.ServiceId.ACI
|
||||
import org.signal.core.models.ServiceId.PNI
|
||||
@@ -86,6 +87,8 @@ class AccountValues internal constructor(store: KeyValueStore, context: Context)
|
||||
private const val KEY_HAS_LINKED_DEVICES = "account.has_linked_devices"
|
||||
private const val KEY_HAS_INACTIVE_PRIMARY_DEVICE_ALERT = "account.has_inactive_primary_device_alert"
|
||||
|
||||
private const val KEY_VERIFICATION_CODE_REQUESTED_AT = "account.verification_code_requested_at"
|
||||
|
||||
private const val KEY_ACCOUNT_ENTROPY_POOL = "account.account_entropy_pool"
|
||||
private const val KEY_RESTORED_ACCOUNT_ENTROPY_KEY = "account.restored_account_entropy_pool"
|
||||
private const val KEY_RESTORED_ACCOUNT_ENTROPY_KEY_FROM_PRIMARY = "account.restore_account_entropy_pool_primary"
|
||||
@@ -562,6 +565,11 @@ class AccountValues internal constructor(store: KeyValueStore, context: Context)
|
||||
@get:JvmName("isMultiDevice")
|
||||
var isMultiDevice by booleanValue(KEY_HAS_LINKED_DEVICES, false)
|
||||
|
||||
/** Server has indicated a verification code was requested for the account at this timestamp (ms since epoch) */
|
||||
private val verificationCodeRequestedAtMsValue = longValue(KEY_VERIFICATION_CODE_REQUESTED_AT, 0)
|
||||
var verificationCodeRequestedAtMs: Long by verificationCodeRequestedAtMsValue
|
||||
val verificationCodeRequestedAtMsFlow: Flow<Long> by lazy { verificationCodeRequestedAtMsValue.toFlow() }
|
||||
|
||||
/** Do not alter. If you need to migrate more stuff, create a new method. */
|
||||
private fun migrateFromSharedPrefsV1(context: Context) {
|
||||
Log.i(TAG, "[V1] Migrating account values from shared prefs.")
|
||||
|
||||
Reference in New Issue
Block a user