mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-07-07 06:25:16 +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.")
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="#FFF8E0D9"
|
||||
android:pathData="M24 0A24 24 0 1 0 24 48 24 24 0 1 0 24 0z"/>
|
||||
<path
|
||||
android:fillColor="#FFFDEEEB"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M24 10.58c-4.03 0-7.3 3.27-7.3 7.3v3.58c-0.73 0.41-1.33 1.03-1.72 1.79-0.25 0.5-0.36 1.03-0.4 1.66-0.06 0.61-0.06 1.37-0.06 2.32v3.45c0 0.96 0 1.71 0.05 2.33 0.05 0.63 0.16 1.17 0.41 1.66 0.4 0.8 1.05 1.44 1.85 1.85 0.5 0.25 1.03 0.36 1.66 0.4 0.62 0.06 1.38 0.06 2.33 0.06h6.36c0.95 0 1.71 0 2.33-0.05 0.63-0.05 1.17-0.16 1.66-0.41 0.8-0.4 1.44-1.05 1.85-1.85 0.25-0.5 0.36-1.03 0.4-1.66 0.06-0.62 0.06-1.37 0.06-2.33v-3.45c0-0.95 0-1.7-0.05-2.32-0.05-0.63-0.16-1.17-0.41-1.66-0.39-0.76-0.99-1.38-1.73-1.79v-3.59c0-4.02-3.26-7.29-7.29-7.29Zm4.96 10.38v-3.09c0-2.73-2.22-4.95-4.96-4.95-2.74 0-4.96 2.22-4.96 4.96v3.08c0.5-0.02 1.1-0.02 1.78-0.02h6.36c0.69 0 1.27 0 1.78 0.02Zm-7 6.69c0-1.13 0.91-2.05 2.04-2.05s2.04 0.92 2.04 2.05c0 0.69-0.34 1.3-0.87 1.67v1.97c0 0.65-0.53 1.17-1.17 1.17-0.64 0-1.17-0.52-1.17-1.17v-1.97c-0.53-0.37-0.87-0.98-0.87-1.67Z"/>
|
||||
<path
|
||||
android:fillColor="#FFC34922"
|
||||
android:pathData="M22.83 29.32c-0.53-0.37-0.87-0.98-0.87-1.67 0-1.13 0.91-2.05 2.04-2.05s2.04 0.92 2.04 2.05c0 0.69-0.34 1.3-0.87 1.67v1.97c0 0.65-0.53 1.17-1.17 1.17-0.64 0-1.17-0.52-1.17-1.17v-1.97Z"/>
|
||||
<path
|
||||
android:fillColor="#FFC34922"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M16.7 17.88c0-4.03 3.27-7.3 7.3-7.3 4.03 0 7.3 3.27 7.3 7.3v3.1L31.36 21c0.88 0.45 1.6 1.16 2.04 2.04 0.29 0.56 0.4 1.17 0.46 1.82 0.05 0.63 0.05 1.4 0.05 2.35v3.48c0 0.94 0 1.71-0.05 2.34-0.06 0.66-0.17 1.26-0.46 1.83-0.45 0.88-1.16 1.6-2.04 2.04-0.57 0.29-1.17 0.4-1.83 0.46-0.63 0.05-1.4 0.05-2.34 0.05h-6.4c-0.94 0-1.71 0-2.34-0.05-0.66-0.06-1.26-0.17-1.83-0.46-0.88-0.45-1.6-1.16-2.04-2.04-0.29-0.57-0.4-1.17-0.46-1.83-0.05-0.63-0.05-1.4-0.05-2.34v-3.48c0-0.94 0-1.72 0.05-2.35 0.06-0.65 0.17-1.26 0.46-1.82 0.45-0.88 1.16-1.6 2.04-2.04l0.08-0.04v-3.1Zm12.26 0v2.64c-0.51-0.02-1.1-0.02-1.76-0.02h-6.4c-0.67 0-1.25 0-1.76 0.02v-2.64c0-2.74 2.22-4.96 4.96-4.96 2.74 0 4.96 2.22 4.96 4.96Zm-11.27 5.2c0.18-0.09 0.44-0.16 0.96-0.2 0.52-0.05 1.2-0.05 2.2-0.05h6.3c1 0 1.68 0 2.2 0.05 0.52 0.04 0.78 0.11 0.96 0.2 0.44 0.23 0.8 0.59 1.02 1.03 0.1 0.18 0.17 0.44 0.21 0.95 0.04 0.53 0.04 1.2 0.04 2.2v3.39c0 1 0 1.68-0.04 2.2-0.04 0.52-0.12 0.78-0.21 0.96-0.22 0.44-0.58 0.8-1.02 1.02-0.18 0.1-0.44 0.17-0.96 0.21-0.52 0.04-1.2 0.04-2.2 0.04h-6.3c-1 0-1.68 0-2.2-0.04-0.52-0.04-0.78-0.12-0.96-0.21-0.44-0.22-0.8-0.58-1.02-1.02-0.1-0.18-0.17-0.44-0.21-0.96-0.04-0.52-0.04-1.2-0.04-2.2v-3.38c0-1 0-1.68 0.04-2.2 0.04-0.52 0.12-0.78 0.21-0.96 0.22-0.44 0.58-0.8 1.02-1.02Z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,20 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="#FFF8E0D9"
|
||||
android:pathData="M48 24c0 13.25-10.75 24-24 24S0 37.25 0 24 10.75 0 24 0s24 10.75 24 24Z"/>
|
||||
<path
|
||||
android:fillColor="#FFFDEEEB"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M24.4 13c-6.17 0-11.17 5-11.17 11.17 0 1.9 0.48 3.7 1.32 5.27 0.24 0.45 0.3 1 0.11 1.5L13 35.56l4.62-1.66c0.5-0.18 1.06-0.13 1.5 0.11 1.57 0.85 3.37 1.32 5.28 1.32 6.16 0 11.16-5 11.16-11.16C35.56 18 30.56 13 24.4 13Z"/>
|
||||
<path
|
||||
android:fillColor="#FFC34922"
|
||||
android:pathData="M19.63 19.63c0.4-0.4 1.05-0.4 1.45 0L24 22.56l2.92-2.93c0.4-0.4 1.05-0.4 1.45 0 0.4 0.4 0.4 1.05 0 1.45L25.44 24l2.93 2.92c0.4 0.4 0.4 1.05 0 1.45-0.4 0.4-1.05 0.4-1.45 0L24 25.44l-2.92 2.93c-0.4 0.4-1.05 0.4-1.45 0-0.4-0.4-0.4-1.05 0-1.45L22.56 24l-2.93-2.92c-0.4-0.4-0.4-1.05 0-1.45Z"/>
|
||||
<path
|
||||
android:fillColor="#FFC34922"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M12.19 24c0-6.52 5.29-11.81 11.81-11.81 6.52 0 11.81 5.29 11.81 11.81 0 6.52-5.29 11.81-11.81 11.81-1.95 0-3.8-0.47-5.41-1.3l-4.74 1.7c-1.28 0.46-2.52-0.78-2.06-2.06l1.7-4.74c-0.83-1.62-1.3-3.46-1.3-5.41ZM24 14.23c-5.4 0-9.77 4.37-9.77 9.77 0 1.67 0.42 3.24 1.15 4.61 0.21 0.4 0.26 0.87 0.1 1.32l-1.45 4.04 4.04-1.45c0.45-0.16 0.92-0.12 1.32 0.1 1.37 0.73 2.94 1.15 4.61 1.15 5.4 0 9.77-4.37 9.77-9.77 0-5.4-4.37-9.77-9.77-9.77Z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,24 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="#FFF8E0D9"
|
||||
android:pathData="M24 0A24 24 0 1 0 24 48 24 24 0 1 0 24 0z"/>
|
||||
<path
|
||||
android:fillColor="#FFFDEEEB"
|
||||
android:pathData="M11 19c0-2.76 2.24-5 5-5h16c2.76 0 5 2.24 5 5v10c0 2.76-2.24 5-5 5H16c-2.76 0-5-2.24-5-5V19Z"/>
|
||||
<path
|
||||
android:fillColor="#FFC34922"
|
||||
android:pathData="M32 31.75V34H16v-2.25h16ZM34.75 29V19c0-1.52-1.23-2.75-2.75-2.75H16c-1.52 0-2.75 1.23-2.75 2.75v10c0 1.52 1.23 2.75 2.75 2.75V34c-2.76 0-5-2.24-5-5V19c0-2.76 2.24-5 5-5h16c2.76 0 5 2.24 5 5v10c0 2.76-2.24 5-5 5v-2.25c1.52 0 2.75-1.23 2.75-2.75Z"/>
|
||||
<path
|
||||
android:fillColor="#FFC34922"
|
||||
android:pathData="M18.01 26.21c-0.38 0-0.63-0.23-0.62-0.57v-0.1l0.16-1.1-1.03 0.7c-0.12 0.1-0.27 0.15-0.42 0.15-0.31 0-0.6-0.24-0.6-0.57 0-0.23 0.16-0.43 0.42-0.53l1.19-0.46-1.19-0.47c-0.27-0.1-0.42-0.3-0.42-0.53 0-0.31 0.29-0.56 0.6-0.56 0.16 0 0.3 0.05 0.42 0.14l1.04 0.72-0.16-1.16-0.01-0.09c0-0.33 0.25-0.57 0.62-0.57s0.6 0.23 0.6 0.57l-0.01 0.1-0.16 1.15 1.04-0.72c0.12-0.1 0.26-0.14 0.42-0.14 0.32 0 0.6 0.24 0.6 0.56 0 0.24-0.15 0.43-0.42 0.53l-1.2 0.47 1.2 0.47c0.27 0.1 0.42 0.29 0.42 0.52 0 0.33-0.29 0.57-0.6 0.57-0.16 0-0.3-0.05-0.42-0.14l-1.03-0.72 0.16 1.12 0.01 0.1c0 0.33-0.24 0.56-0.6 0.56Z"/>
|
||||
<path
|
||||
android:fillColor="#FFC34922"
|
||||
android:pathData="M24.01 26.21c-0.38 0-0.63-0.23-0.62-0.57v-0.1l0.16-1.1-1.03 0.7c-0.12 0.1-0.27 0.15-0.42 0.15-0.31 0-0.6-0.24-0.6-0.57 0-0.23 0.16-0.43 0.42-0.53l1.19-0.46-1.19-0.47c-0.27-0.1-0.42-0.3-0.42-0.53 0-0.31 0.29-0.56 0.6-0.56 0.16 0 0.3 0.05 0.42 0.14l1.04 0.72-0.16-1.16-0.01-0.09c0-0.33 0.25-0.57 0.62-0.57s0.6 0.23 0.6 0.57l-0.01 0.1-0.16 1.15 1.04-0.72c0.12-0.1 0.26-0.14 0.42-0.14 0.32 0 0.6 0.24 0.6 0.56 0 0.24-0.15 0.43-0.42 0.53l-1.2 0.47 1.2 0.47c0.27 0.1 0.42 0.29 0.42 0.52 0 0.33-0.29 0.57-0.6 0.57-0.16 0-0.3-0.05-0.42-0.14l-1.03-0.72 0.16 1.12 0.01 0.1c0 0.33-0.24 0.56-0.6 0.56Z"/>
|
||||
<path
|
||||
android:fillColor="#FFC34922"
|
||||
android:pathData="M30.01 26.21c-0.38 0-0.63-0.23-0.62-0.57v-0.1l0.16-1.1-1.03 0.7c-0.12 0.1-0.27 0.15-0.42 0.15-0.31 0-0.6-0.24-0.6-0.57 0-0.23 0.16-0.43 0.42-0.53l1.19-0.46-1.19-0.47c-0.27-0.1-0.42-0.3-0.42-0.53 0-0.31 0.29-0.56 0.6-0.56 0.16 0 0.3 0.05 0.42 0.14l1.04 0.72-0.16-1.16-0.01-0.09c0-0.33 0.25-0.57 0.62-0.57s0.6 0.23 0.6 0.57l-0.01 0.1-0.16 1.15 1.04-0.72c0.12-0.1 0.26-0.14 0.42-0.14 0.32 0 0.6 0.24 0.6 0.56 0 0.24-0.15 0.43-0.42 0.53l-1.2 0.47 1.2 0.47c0.27 0.1 0.42 0.29 0.42 0.52 0 0.33-0.29 0.57-0.6 0.57-0.16 0-0.3-0.05-0.42-0.14l-1.03-0.72 0.16 1.12 0.01 0.1c0 0.33-0.24 0.56-0.6 0.56Z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,31 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="96dp"
|
||||
android:height="96dp"
|
||||
android:viewportWidth="96"
|
||||
android:viewportHeight="96">
|
||||
<path
|
||||
android:fillColor="#FFC3C6FA"
|
||||
android:strokeColor="#FF3B45FD"
|
||||
android:strokeWidth="2.5"
|
||||
android:pathData="M59 10.75c4.56 0 8.25 3.7 8.25 8.25v58c0 4.56-3.7 8.25-8.25 8.25H37c-4.56 0-8.25-3.7-8.25-8.25V19c0-4.56 3.7-8.25 8.25-8.25h22Z"/>
|
||||
<path
|
||||
android:fillColor="#FFDEE0FC"
|
||||
android:pathData="M49.92 12C48.23 22.66 40.3 31.25 30 33.9V19c0-3.87 3.13-7 7-7h12.92Z"/>
|
||||
<path
|
||||
android:fillColor="#FFA9ADF8"
|
||||
android:pathData="M62.36 12.86C64.53 14.05 66 16.36 66 19v58c0 3.87-3.13 7-7 7H37c-1.6 0-3.08-0.54-4.27-1.45 1.46-2.02 3.82-3.34 6.5-3.34h15c4.43 0 8-3.58 8-8V14.23c0-0.47 0.05-0.93 0.13-1.37Z"/>
|
||||
<path
|
||||
android:fillColor="#FF3B45FD"
|
||||
android:pathData="M43 16.5c0-0.83 0.67-1.5 1.5-1.5h7c0.83 0 1.5 0.67 1.5 1.5v0c0 0.83-0.67 1.5-1.5 1.5h-7c-0.83 0-1.5-0.67-1.5-1.5z"/>
|
||||
<path
|
||||
android:fillColor="#FFC34922"
|
||||
android:strokeColor="#FF953516"
|
||||
android:strokeWidth="2"
|
||||
android:pathData="M66 61c8.28 0 15 6.72 15 15 0 8.28-6.72 15-15 15-8.28 0-15-6.72-15-15 0-8.28 6.72-15 15-15Z"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M60.87 73.32c0.1-2.92 2.41-5 5.13-5 2.72 0 5.02 2.08 5.13 5 0.1 2.76 0.65 3.8 1.12 4.36 0.12 0.15 0.24 0.27 0.37 0.38l0.03 0.03 0.03 0.02 0.3 0.3c0.25 0.28 0.43 0.6 0.43 1.12 0 0.84-0.68 1.53-1.52 1.53H60.11c-0.84 0-1.52-0.7-1.52-1.53 0-0.52 0.18-0.84 0.43-1.12l0.3-0.3 0.06-0.05c0.13-0.11 0.25-0.23 0.37-0.38 0.47-0.55 1.03-1.6 1.12-4.36Z"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M63.71 82.37c0.07-0.11 0.2-0.18 0.33-0.18h3.92c0.14 0 0.26 0.07 0.33 0.18 0.06 0.12 0.07 0.26 0 0.38-0.45 0.8-1.3 1.31-2.29 1.31-1 0-1.84-0.51-2.3-1.31-0.06-0.12-0.05-0.26 0.01-0.38Z"/>
|
||||
</vector>
|
||||
@@ -845,6 +845,16 @@
|
||||
app:popUpTo="@id/app_settings"
|
||||
app:popUpToInclusive="true" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_direct_to_accountSettingsFragment"
|
||||
app:destination="@id/accountSettingsFragment"
|
||||
app:enterAnim="@anim/fragment_open_enter"
|
||||
app:exitAnim="@anim/fragment_open_exit"
|
||||
app:popEnterAnim="@anim/fragment_close_enter"
|
||||
app:popExitAnim="@anim/fragment_close_exit"
|
||||
app:popUpTo="@id/app_settings"
|
||||
app:popUpToInclusive="true" />
|
||||
|
||||
<!-- endregion -->
|
||||
|
||||
<!-- Labs Settings -->
|
||||
|
||||
@@ -9902,5 +9902,35 @@
|
||||
<!-- Body of the sheet shown when a local backup restore could not be completed, explaining how to try again -->
|
||||
<string name="CouldNotCompleteBackupRestoreSheet__body_retry">To try again, uninstall and re-install Signal on this device, and choose \"Restore or transfer\".</string>
|
||||
|
||||
<!-- Title of the sheet shown when the server reports a verification code was requested for the user's phone number -->
|
||||
<string name="VerificationCodeRequestedBottomSheet__title">A verification code was requested</string>
|
||||
<!-- First paragraph of the body of the verification code requested sheet -->
|
||||
<string name="VerificationCodeRequestedBottomSheet__body_1">Do not give your verification code to anyone. Signal will never message you for it. If you received a message from someone pretending to be Signal, it is a scam.</string>
|
||||
<!-- Second paragraph of the body of the verification code requested sheet -->
|
||||
<string name="VerificationCodeRequestedBottomSheet__body_2">You can safely ignore this message if you requested the code yourself.</string>
|
||||
<!-- Button on the verification code requested sheet that opens the safety tips screen -->
|
||||
<string name="VerificationCodeRequestedBottomSheet__safety_tips">Safety tips</string>
|
||||
<!-- Button on the verification code requested sheet that dismisses the sheet -->
|
||||
<string name="VerificationCodeRequestedBottomSheet__ok">OK</string>
|
||||
<!-- Subtitle on the verification code requested sheet showing the time the code was requested followed by the relative day. %1$s is the time (e.g. "3:25 PM"), %2$s is the relative day (e.g. "Today", "Yesterday", "Jan 15") -->
|
||||
<string name="VerificationCodeRequestedBottomSheet__time_with_day">%1$s %2$s</string>
|
||||
|
||||
<!-- Title of the safety tips screen within the verification code requested sheet -->
|
||||
<string name="SafetyTipsBottomSheet__title">Safety Tips</string>
|
||||
<!-- Title of the first safety tip: don't respond to chats claiming to be Signal -->
|
||||
<string name="SafetyTipsBottomSheet__tip_1_title">Don\'t respond to chats from Signal</string>
|
||||
<!-- Body of the first safety tip -->
|
||||
<string name="SafetyTipsBottomSheet__tip_1_body">Signal will never message you for your registration code, PIN, or recovery key. Never respond to a chat pretending to be Signal.</string>
|
||||
<!-- Title of the second safety tip: keep your verification code safe -->
|
||||
<string name="SafetyTipsBottomSheet__tip_2_title">Keep your verification code safe</string>
|
||||
<!-- Body of the second safety tip -->
|
||||
<string name="SafetyTipsBottomSheet__tip_2_body">If you received a verification code you didn\'t request, someone may be attempting to access your account. Do not share your code.</string>
|
||||
<!-- Title of the third safety tip: enable registration lock -->
|
||||
<string name="SafetyTipsBottomSheet__tip_3_title">Turn on registration lock in account settings</string>
|
||||
<!-- Body of the third safety tip -->
|
||||
<string name="SafetyTipsBottomSheet__tip_3_body">Protect your account by requiring your Signal PIN, in addition to your verification code, when registering with Signal.</string>
|
||||
<!-- Button on the safety tips screen that opens account settings -->
|
||||
<string name="SafetyTipsBottomSheet__open_account_settings">Open account settings</string>
|
||||
|
||||
<!-- EOF -->
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user