mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-05-25 09:38:54 +01:00
Enforce change number post registration delay.
This commit is contained in:
committed by
jeffrey-signal
parent
4decae274b
commit
a9649fd017
+16
-3
@@ -33,6 +33,7 @@ import org.thoughtcrime.securesms.registration.sms.ReceivedSmsEvent
|
||||
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
import org.thoughtcrime.securesms.util.visible
|
||||
import kotlin.math.ceil
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
/**
|
||||
@@ -168,7 +169,7 @@ class ChangeNumberEnterCodeFragment : LoggingFragment(R.layout.fragment_change_n
|
||||
is ChangeNumberResult.RegistrationLocked -> presentRegistrationLocked(result.timeRemaining)
|
||||
is ChangeNumberResult.AuthorizationFailed -> presentIncorrectCodeDialog()
|
||||
is ChangeNumberResult.AttemptsExhausted -> presentAccountLocked()
|
||||
is ChangeNumberResult.RateLimited -> presentRateLimitedDialog()
|
||||
is ChangeNumberResult.RateLimited -> presentRateLimitedDialog(result.timeRemaining)
|
||||
|
||||
else -> presentGenericError(result)
|
||||
}
|
||||
@@ -195,13 +196,25 @@ class ChangeNumberEnterCodeFragment : LoggingFragment(R.layout.fragment_change_n
|
||||
)
|
||||
}
|
||||
|
||||
private fun presentRateLimitedDialog() {
|
||||
private fun presentRateLimitedDialog(retryAfterSeconds: Long = 0) {
|
||||
binding.codeEntryLayout.keyboard.displayFailure().addListener(
|
||||
object : AssertedSuccessListener<Boolean?>() {
|
||||
override fun onSuccess(result: Boolean?) {
|
||||
MaterialAlertDialogBuilder(requireContext()).apply {
|
||||
setTitle(R.string.RegistrationActivity_too_many_attempts)
|
||||
setMessage(R.string.RegistrationActivity_you_have_made_too_many_attempts_please_try_again_later)
|
||||
if (retryAfterSeconds > 0) {
|
||||
val minutes = ceil(retryAfterSeconds / 60.0).toInt().coerceAtLeast(1)
|
||||
setMessage(
|
||||
if (minutes >= 60) {
|
||||
val hours = ceil(minutes / 60.0).toInt()
|
||||
resources.getQuantityString(R.plurals.ChangeNumberEnterCodeFragment__too_many_attempts_try_again_in_hours, hours, hours)
|
||||
} else {
|
||||
resources.getQuantityString(R.plurals.ChangeNumberEnterCodeFragment__too_many_attempts_try_again_in_minutes, minutes, minutes)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
setMessage(R.string.RegistrationActivity_you_have_made_too_many_attempts_please_try_again_later)
|
||||
}
|
||||
setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
|
||||
binding.codeEntryLayout.callMeCountDown.visibility = View.VISIBLE
|
||||
binding.codeEntryLayout.resendSmsCountDown.visibility = View.VISIBLE
|
||||
|
||||
+20
-4
@@ -23,7 +23,6 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
@@ -34,7 +33,10 @@ import org.signal.core.ui.compose.DayNightPreviews
|
||||
import org.signal.core.ui.compose.Previews
|
||||
import org.signal.core.ui.compose.Scaffolds
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
class ChangeNumberFragment : ComposeFragment() {
|
||||
|
||||
@@ -46,10 +48,25 @@ class ChangeNumberFragment : ComposeFragment() {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onContinueClick = {
|
||||
navController.safeNavigate(ChangeNumberFragmentDirections.actionChangePhoneNumberFragmentToEnterPhoneNumberChangeFragment())
|
||||
val remainingWaitSeconds = remainingPostRegistrationWaitSeconds()
|
||||
if (remainingWaitSeconds > 0) {
|
||||
ChangeNumberPostRegistrationWaitSheet.show(parentFragmentManager, remainingWaitSeconds)
|
||||
} else {
|
||||
navController.safeNavigate(ChangeNumberFragmentDirections.actionChangePhoneNumberFragmentToEnterPhoneNumberChangeFragment())
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun remainingPostRegistrationWaitSeconds(): Long {
|
||||
val registeredAt = SignalStore.account.registeredAtTimestamp
|
||||
if (registeredAt <= 0) {
|
||||
return 0
|
||||
}
|
||||
val waitingPeriodSeconds = RemoteConfig.changeNumberPostRegistrationWaitingPeriodSeconds
|
||||
val elapsedSeconds = (System.currentTimeMillis() - registeredAt).milliseconds.inWholeSeconds
|
||||
return (waitingPeriodSeconds - elapsedSeconds).coerceAtLeast(0)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -73,7 +90,7 @@ fun ChangeNumberScreen(
|
||||
.padding(horizontal = 32.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.change_number_hero_image),
|
||||
painter = painterResource(id = R.drawable.change_number),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(top = 20.dp)
|
||||
@@ -83,7 +100,6 @@ fun ChangeNumberScreen(
|
||||
text = stringResource(id = R.string.AccountSettingsFragment__change_phone_number),
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.padding(top = 24.dp)
|
||||
)
|
||||
|
||||
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.settings.app.changenumber
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.res.painterResource
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
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.signal.core.ui.compose.horizontalGutters
|
||||
import org.thoughtcrime.securesms.R
|
||||
import kotlin.math.ceil
|
||||
|
||||
/**
|
||||
* Sheet shown when the user attempts to change their phone number before the
|
||||
* post-registration waiting period has elapsed.
|
||||
*/
|
||||
class ChangeNumberPostRegistrationWaitSheet : ComposeBottomSheetDialogFragment() {
|
||||
|
||||
companion object {
|
||||
private const val ARG_REMAINING_SECONDS = "arg.remaining_seconds"
|
||||
|
||||
@JvmStatic
|
||||
fun show(fragmentManager: FragmentManager, remainingSeconds: Long) {
|
||||
ChangeNumberPostRegistrationWaitSheet().apply {
|
||||
arguments = Bundle().apply {
|
||||
putLong(ARG_REMAINING_SECONDS, remainingSeconds)
|
||||
}
|
||||
}.show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
|
||||
}
|
||||
}
|
||||
|
||||
private val remainingSeconds: Long
|
||||
get() = requireArguments().getLong(ARG_REMAINING_SECONDS)
|
||||
|
||||
@Composable
|
||||
override fun SheetContent() {
|
||||
SheetContent(
|
||||
remainingSeconds = remainingSeconds,
|
||||
onDismiss = { dismissAllowingStateLoss() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SheetContent(
|
||||
remainingSeconds: Long,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.horizontalGutters(gutterSize = 36.dp)
|
||||
) {
|
||||
BottomSheets.Handle()
|
||||
|
||||
Image(
|
||||
painter = painterResource(R.drawable.change_number_error),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(top = 26.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.ChangeNumberPostRegistrationWaitSheet__title),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.ChangeNumberPostRegistrationWaitSheet__body),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(top = 12.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = formatTryAgainIn(remainingSeconds),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
)
|
||||
|
||||
Buttons.LargeTonal(
|
||||
onClick = onDismiss,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 32.dp, bottom = 24.dp, start = 12.dp, end = 12.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.ChangeNumberPostRegistrationWaitSheet__ok))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
private fun SheetContentMinutesPreview() {
|
||||
Previews.BottomSheetContentPreview {
|
||||
SheetContent(
|
||||
remainingSeconds = 25 * 60,
|
||||
onDismiss = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
private fun SheetContentHoursPreview() {
|
||||
Previews.BottomSheetContentPreview {
|
||||
SheetContent(
|
||||
remainingSeconds = 2 * 60 * 60,
|
||||
onDismiss = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun formatTryAgainIn(remainingSeconds: Long): String {
|
||||
val minutes = ceil(remainingSeconds / 60.0).toInt().coerceAtLeast(1)
|
||||
return if (minutes >= 60) {
|
||||
val hours = ceil(minutes / 60.0).toInt()
|
||||
pluralStringResource(R.plurals.ChangeNumberPostRegistrationWaitSheet__try_again_in_hours, hours, hours)
|
||||
} else {
|
||||
pluralStringResource(R.plurals.ChangeNumberPostRegistrationWaitSheet__try_again_in_minutes, minutes, minutes)
|
||||
}
|
||||
}
|
||||
@@ -434,7 +434,7 @@ class AccountValues internal constructor(store: KeyValueStore, context: Context)
|
||||
val isRegistered: Boolean
|
||||
get() = getBoolean(KEY_IS_REGISTERED, false)
|
||||
|
||||
fun setRegistered(registered: Boolean) {
|
||||
fun setRegistered(registered: Boolean, isAciChanged: Boolean = false) {
|
||||
Log.i(TAG, "Setting push registered: $registered", Throwable())
|
||||
|
||||
val previous = isRegistered
|
||||
@@ -451,7 +451,7 @@ class AccountValues internal constructor(store: KeyValueStore, context: Context)
|
||||
clearLocalCredentials()
|
||||
}
|
||||
|
||||
if (!previous && registered) {
|
||||
if (registered && (!previous || isAciChanged)) {
|
||||
registeredAtTimestamp = System.currentTimeMillis()
|
||||
} else if (!registered) {
|
||||
registeredAtTimestamp = -1
|
||||
|
||||
+2
-1
@@ -184,6 +184,7 @@ object RegistrationRepository {
|
||||
val aci: ACI = ACI.parseOrThrow(data.aci)
|
||||
val pni: PNI = PNI.parseOrThrow(data.pni)
|
||||
val hasPin: Boolean = data.hasPin
|
||||
val isAciChanged: Boolean = SignalStore.account.aci != aci
|
||||
|
||||
SignalStore.account.setAci(aci)
|
||||
SignalStore.account.setPni(pni)
|
||||
@@ -232,7 +233,7 @@ object RegistrationRepository {
|
||||
}
|
||||
|
||||
SignalStore.account.setServicePassword(data.servicePassword)
|
||||
SignalStore.account.setRegistered(true)
|
||||
SignalStore.account.setRegistered(registered = true, isAciChanged = isAciChanged)
|
||||
TextSecurePreferences.setPromptedPushRegistration(context, true)
|
||||
TextSecurePreferences.setUnauthorizedReceived(context, false)
|
||||
NotificationManagerCompat.from(context).cancel(NotificationIds.UNREGISTERED_NOTIFICATION_ID)
|
||||
|
||||
@@ -1411,6 +1411,15 @@ object RemoteConfig {
|
||||
hotSwappable = true
|
||||
)
|
||||
|
||||
/** Seconds after registration during which change-number is blocked. */
|
||||
@JvmStatic
|
||||
@get:JvmName("changeNumberPostRegistrationWaitingPeriodSeconds")
|
||||
val changeNumberPostRegistrationWaitingPeriodSeconds: Long by remoteLong(
|
||||
key = "global.changeNumber.postRegistrationWaitingPeriodSeconds",
|
||||
defaultValue = 3600,
|
||||
hotSwappable = true
|
||||
)
|
||||
|
||||
/**
|
||||
* A ratio between 0 and 1, where 0 means that a session is never archived due
|
||||
* to a lack of PQ, and 1 means that a session is always archived due to a
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="160dp"
|
||||
android:height="120dp"
|
||||
android:viewportWidth="160"
|
||||
android:viewportHeight="120">
|
||||
<path
|
||||
android:fillColor="#FF585B7F"
|
||||
android:pathData="M66.15 68.52c-0.26 0.26-0.4 0.61-0.4 0.98s0.14 0.72 0.4 0.98l7.28 7.36c0.54 0.55 1.4 0.55 1.94 0 0.53-0.54 0.53-1.41 0-1.95l-5.04-5.1 1.25 0.1h0.01 21.29c0.76 0 1.37-0.63 1.37-1.39 0-0.76-0.61-1.38-1.37-1.38H71.59l-1.26 0.09 5.04-5.1c0.53-0.54 0.53-1.41 0-1.95-0.54-0.55-1.4-0.55-1.94 0l-7.28 7.36Z"/>
|
||||
<path
|
||||
android:fillColor="#FF585B7F"
|
||||
android:pathData="M93.85 49.52c0.26 0.26 0.4 0.61 0.4 0.98s-0.14 0.72-0.4 0.98l-7.28 7.36c-0.54 0.55-1.4 0.55-1.94 0-0.53-0.54-0.53-1.41 0-1.95l5.04-5.1-1.25 0.1H88.4 67.12c-0.76 0-1.37-0.63-1.37-1.39 0-0.76 0.61-1.38 1.37-1.38h21.29l1.26 0.09-5.04-5.1c-0.53-0.54-0.53-1.41 0-1.95 0.54-0.55 1.4-0.55 1.94 0l7.28 7.36Z"/>
|
||||
<path
|
||||
android:fillColor="#FFC3C6FA"
|
||||
android:pathData="M16.5 31c0-3.87 3.13-7 7-7h22c3.87 0 7 3.13 7 7v58c0 3.87-3.13 7-7 7h-22c-3.87 0-7-3.13-7-7V31Z"/>
|
||||
<path
|
||||
android:fillColor="#FF3B45FD"
|
||||
android:pathData="M14 89V31c0-5.25 4.25-9.5 9.5-9.5V24c-3.87 0-7 3.13-7 7v58c0 3.75 2.94 6.8 6.64 7h0.36 22c3.87 0 7-3.13 7-7V31c0-3.87-3.13-7-7-7v-2.5c5.25 0 9.5 4.25 9.5 9.5v58c0 5.25-4.25 9.5-9.5 9.5h-22c-5.25 0-9.5-4.25-9.5-9.5Zm31.5-67.5V24h-22v-2.5h22Z"/>
|
||||
<path
|
||||
android:fillColor="#FFDEE0FC"
|
||||
android:pathData="M36.42 24C34.73 34.66 26.8 43.25 16.5 45.9V31c0-3.87 3.13-7 7-7h12.92Z"/>
|
||||
<path
|
||||
android:fillColor="#FFA9ADF8"
|
||||
android:pathData="M48.86 24.86c2.17 1.19 3.64 3.5 3.64 6.14v58c0 3.87-3.13 7-7 7h-22c-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-8V26.23c0-0.47 0.05-0.93 0.13-1.37Z"/>
|
||||
<path
|
||||
android:fillColor="#FF3B45FD"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M25.46 49.18c1.46-1.45 3.85-1.29 5.1 0.35l2.53 3.32c1.02 1.35 0.9 3.25-0.3 4.45l-1.23 1.23c0.04 0.1 0.1 0.26 0.21 0.45 0.32 0.58 0.9 1.33 1.66 2.1 0.76 0.75 1.51 1.33 2.09 1.65 0.2 0.1 0.34 0.17 0.45 0.21l1.23-1.23c1.2-1.2 3.1-1.32 4.45-0.3l3.32 2.53c1.64 1.25 1.8 3.64 0.35 5.1l-0.5 0.5c-1.84 1.83-4.57 2.84-7.2 1.94-3.28-1.13-6.37-3-8.98-5.62-2.62-2.61-4.5-5.7-5.62-8.98-0.9-2.63 0.1-5.36 1.94-7.2l0.5-0.5Zm10.7 13.8h-0.02 0.01Zm-4.64-4.63v0.01-0.01Z"/>
|
||||
<path
|
||||
android:fillColor="#FF3B45FD"
|
||||
android:pathData="M29.5 28.5c0-0.83 0.67-1.5 1.5-1.5h7c0.83 0 1.5 0.67 1.5 1.5S38.83 30 38 30h-7c-0.83 0-1.5-0.67-1.5-1.5Z"/>
|
||||
<path
|
||||
android:fillColor="#FFC3C6FA"
|
||||
android:pathData="M107.5 31c0-3.87 3.13-7 7-7h22c3.87 0 7 3.13 7 7v58c0 3.87-3.13 7-7 7h-22c-3.87 0-7-3.13-7-7V31Z"/>
|
||||
<path
|
||||
android:fillColor="#FF3B45FD"
|
||||
android:pathData="M105 89V31c0-5.25 4.25-9.5 9.5-9.5V24c-3.87 0-7 3.13-7 7v58c0 3.75 2.94 6.8 6.64 7h0.36 22c3.87 0 7-3.13 7-7V31c0-3.87-3.13-7-7-7v-2.5c5.25 0 9.5 4.25 9.5 9.5v58c0 5.25-4.25 9.5-9.5 9.5h-22c-5.25 0-9.5-4.25-9.5-9.5Zm31.5-67.5V24h-22v-2.5h22Z"/>
|
||||
<path
|
||||
android:fillColor="#FFDEE0FC"
|
||||
android:pathData="M127.42 24c-1.69 10.66-9.61 19.25-19.92 21.9V31c0-3.87 3.13-7 7-7h12.92Z"/>
|
||||
<path
|
||||
android:fillColor="#FFA9ADF8"
|
||||
android:pathData="M139.86 24.86c2.17 1.19 3.64 3.5 3.64 6.14v58c0 3.87-3.13 7-7 7h-22c-1.6 0-3.08-0.54-4.27-1.45 1.46-2.02 3.83-3.34 6.5-3.34h15c4.43 0 8-3.58 8-8V26.23c0-0.47 0.05-0.93 0.13-1.37Z"/>
|
||||
<path
|
||||
android:fillColor="#FF3B45FD"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M116.46 49.18c1.46-1.45 3.86-1.29 5.1 0.35l2.53 3.32c1.02 1.35 0.9 3.25-0.3 4.45l-1.23 1.23c0.04 0.1 0.1 0.26 0.21 0.45 0.32 0.58 0.9 1.33 1.66 2.1 0.76 0.75 1.51 1.33 2.09 1.65 0.2 0.1 0.34 0.17 0.45 0.21l1.23-1.23c1.2-1.2 3.1-1.32 4.45-0.3l3.32 2.53c1.64 1.25 1.8 3.64 0.35 5.1l-0.5 0.5c-1.84 1.83-4.57 2.84-7.2 1.94-3.28-1.13-6.37-3-8.98-5.62-2.62-2.61-4.5-5.7-5.62-8.98-0.9-2.63 0.1-5.36 1.94-7.2l0.5-0.5Zm10.7 13.8h-0.02 0.01Zm-4.64-4.63v0.01-0.01Z"/>
|
||||
<path
|
||||
android:fillColor="#FF3B45FD"
|
||||
android:pathData="M120.5 28.5c0-0.83 0.67-1.5 1.5-1.5h7c0.83 0 1.5 0.67 1.5 1.5S129.83 30 129 30h-7c-0.83 0-1.5-0.67-1.5-1.5Z"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M28.93 50.76c-0.5-0.65-1.45-0.71-2.02-0.14l-0.51 0.51c-1.42 1.42-2.04 3.37-1.45 5.09 1.03 3 2.74 5.8 5.13 8.2 2.4 2.4 5.2 4.1 8.2 5.13 1.72 0.59 3.67-0.03 5.09-1.45l0.5-0.5c0.58-0.58 0.52-1.54-0.13-2.03l-3.33-2.53c-0.54-0.4-1.29-0.36-1.77 0.12L37.2 64.6c-0.48 0.48-1.11 0.44-1.47 0.37-0.4-0.07-0.8-0.25-1.2-0.47-0.78-0.43-1.68-1.14-2.54-2-0.85-0.85-1.56-1.75-2-2.54-0.2-0.38-0.39-0.8-0.46-1.2-0.07-0.35-0.11-0.98 0.37-1.46l1.45-1.45c0.48-0.48 0.53-1.23 0.12-1.77l-2.53-3.33Z"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M119.93 50.76c-0.5-0.65-1.45-0.71-2.02-0.14l-0.51 0.51c-1.42 1.42-2.04 3.37-1.45 5.09 1.03 3 2.74 5.8 5.13 8.2 2.4 2.4 5.2 4.1 8.2 5.13 1.72 0.59 3.67-0.03 5.09-1.45l0.5-0.5c0.58-0.58 0.52-1.54-0.13-2.03l-3.33-2.53c-0.54-0.4-1.29-0.36-1.77 0.12l-1.44 1.45c-0.49 0.48-1.12 0.44-1.48 0.37-0.4-0.07-0.8-0.25-1.2-0.47-0.78-0.43-1.68-1.14-2.53-2-0.86-0.85-1.57-1.75-2-2.54-0.22-0.38-0.4-0.8-0.47-1.2-0.07-0.35-0.11-0.98 0.37-1.46l1.45-1.45c0.48-0.48 0.53-1.23 0.12-1.77l-2.53-3.33Z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,40 @@
|
||||
<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:pathData="M29 19c0-3.87 3.13-7 7-7h22c3.87 0 7 3.13 7 7v58c0 3.87-3.13 7-7 7H36c-3.87 0-7-3.13-7-7V19Z"/>
|
||||
<path
|
||||
android:fillColor="#FF3B45FD"
|
||||
android:pathData="M26.5 77V19c0-5.25 4.25-9.5 9.5-9.5V12c-3.87 0-7 3.13-7 7v58c0 3.75 2.94 6.8 6.64 7H36h22c3.87 0 7-3.13 7-7V19c0-3.87-3.13-7-7-7V9.5c5.25 0 9.5 4.25 9.5 9.5v58c0 5.25-4.25 9.5-9.5 9.5H36c-5.25 0-9.5-4.25-9.5-9.5ZM58 9.5V12H36V9.5h22Z"/>
|
||||
<path
|
||||
android:fillColor="#FFDEE0FC"
|
||||
android:pathData="M48.92 12C47.23 22.66 39.3 31.25 29 33.9V19c0-3.87 3.13-7 7-7h12.92Z"/>
|
||||
<path
|
||||
android:fillColor="#FFA9ADF8"
|
||||
android:pathData="M61.36 12.86C63.53 14.05 65 16.36 65 19v58c0 3.87-3.13 7-7 7H36c-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:fillType="evenOdd"
|
||||
android:pathData="M37.96 37.18c1.46-1.45 3.85-1.29 5.1 0.35l2.53 3.32c1.02 1.35 0.9 3.25-0.3 4.45l-1.23 1.23c0.04 0.1 0.1 0.26 0.21 0.45 0.32 0.58 0.9 1.33 1.66 2.1 0.76 0.75 1.51 1.33 2.09 1.65 0.2 0.1 0.34 0.17 0.45 0.21l1.23-1.23c1.2-1.2 3.1-1.32 4.45-0.3l3.32 2.53c1.64 1.25 1.8 3.64 0.35 5.1l-0.5 0.5c-1.84 1.83-4.57 2.84-7.2 1.94-3.28-1.13-6.37-3-8.98-5.62-2.62-2.61-4.5-5.7-5.62-8.98-0.9-2.63 0.1-5.36 1.94-7.2l0.5-0.5Zm10.7 13.8h-0.02 0.01Zm-4.64-4.63v0.01-0.01Z"/>
|
||||
<path
|
||||
android:fillColor="#FF3B45FD"
|
||||
android:pathData="M42 16.5c0-0.83 0.67-1.5 1.5-1.5h7c0.83 0 1.5 0.67 1.5 1.5S51.33 18 50.5 18h-7c-0.83 0-1.5-0.67-1.5-1.5Z"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M41.43 38.76c-0.5-0.65-1.45-0.71-2.02-0.14l-0.51 0.51c-1.42 1.42-2.04 3.37-1.45 5.09 1.03 3 2.74 5.8 5.13 8.2 2.4 2.4 5.2 4.1 8.2 5.13 1.72 0.59 3.67-0.03 5.09-1.45l0.5-0.5c0.58-0.58 0.52-1.54-0.13-2.03l-3.33-2.53c-0.54-0.4-1.29-0.36-1.77 0.12L49.7 52.6c-0.48 0.48-1.11 0.44-1.47 0.37-0.4-0.07-0.8-0.25-1.2-0.47-0.78-0.43-1.68-1.14-2.54-2-0.85-0.85-1.56-1.75-2-2.54-0.2-0.38-0.39-0.8-0.46-1.2-0.07-0.35-0.11-0.98 0.37-1.46l1.45-1.45c0.48-0.48 0.53-1.23 0.12-1.77l-2.53-3.33Z"/>
|
||||
<path
|
||||
android:fillColor="#FF686FDB"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M68 64c-7.73 0-14 6.27-14 14s6.27 14 14 14 14-6.27 14-14-6.27-14-14-14Z"/>
|
||||
<path
|
||||
android:fillColor="#FF4B53DE"
|
||||
android:pathData="M68 64c7.73 0 14 6.27 14 14s-6.27 14-14 14-14-6.27-14-14 6.27-14 14-14Zm0 2c-6.63 0-12 5.37-12 12s5.37 12 12 12 12-5.37 12-12-5.37-12-12-12Z"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M73.54 74.54c0.5-0.51 0.5-1.33 0-1.84-0.51-0.5-1.33-0.5-1.84 0l-3.58 3.58-3.58-3.58c-0.51-0.5-1.33-0.5-1.84 0-0.5 0.5-0.5 1.33 0 1.84l3.58 3.58-3.58 3.58c-0.5 0.5-0.5 1.33 0 1.84 0.5 0.5 1.33 0.5 1.84 0l3.58-3.58 3.58 3.58c0.5 0.5 1.33 0.5 1.84 0 0.5-0.51 0.5-1.33 0-1.84l-3.58-3.58 3.58-3.58Z"/>
|
||||
</vector>
|
||||
@@ -1,19 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="112dp"
|
||||
android:height="112dp"
|
||||
android:viewportWidth="112"
|
||||
android:viewportHeight="112">
|
||||
<path
|
||||
android:pathData="M56,56m-56,0a56,56 0,1 1,112 0a56,56 0,1 1,-112 0"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.05"/>
|
||||
<path
|
||||
android:pathData="M69.3,16.1L42.9,16C39.1,16 36,19.1 36,22.9L35.9,89C35.9,92.8 39,95.9 42.8,95.9L69.1,96C72.9,96 76,92.9 76,89.1L76.2,23C76.2,19.2 73.1,16.1 69.3,16.1ZM72.8,89.1C72.8,91.1 71.1,92.8 69.1,92.8L42.7,92.7C40.7,92.7 39,91 39,89L39.1,22.9C39.1,20.9 40.8,19.2 42.8,19.2L69.2,19.3C71.2,19.3 72.9,21 72.9,23L72.8,89.1Z"
|
||||
android:fillColor="#B9B9B9"/>
|
||||
<path
|
||||
android:pathData="M72.8,89.1C72.8,91.1 71.1,92.8 69.1,92.8L42.7,92.7C40.7,92.7 39,91 39,89L39.1,22.9C39.1,20.9 40.8,19.2 42.8,19.2L69.2,19.3C71.2,19.3 72.9,21 72.9,23L72.8,89.1Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M46.31,47.42C45.72,48.25 45,49.8 46,52.83C47.15,55.825 48.917,58.546 51.186,60.814C53.454,63.083 56.175,64.85 59.17,66C62.17,67.07 63.75,66.3 64.58,65.71C65.309,65.209 65.887,64.517 66.25,63.71C66.496,63.238 66.562,62.692 66.435,62.175C66.308,61.658 65.997,61.204 65.56,60.9L62.13,58.5C61.701,58.195 61.176,58.055 60.653,58.106C60.129,58.158 59.641,58.397 59.28,58.78C58.89,59.19 58.64,59.47 58.28,59.78C58.102,59.996 57.849,60.138 57.571,60.177C57.293,60.216 57.011,60.149 56.78,59.99C55.86,59.307 54.991,58.558 54.18,57.75C53.393,56.943 52.664,56.081 52,55.17C51.841,54.939 51.774,54.657 51.813,54.379C51.852,54.101 51.994,53.848 52.21,53.67C52.56,53.36 52.84,53.11 53.21,52.72C53.593,52.359 53.832,51.871 53.884,51.347C53.935,50.824 53.795,50.299 53.49,49.87L51.12,46.44C50.816,46.003 50.362,45.692 49.845,45.565C49.328,45.438 48.782,45.504 48.31,45.75C47.503,46.113 46.811,46.691 46.31,47.42Z"
|
||||
android:fillColor="#2C6BED"/>
|
||||
</vector>
|
||||
@@ -5728,6 +5728,33 @@
|
||||
<!-- Confirmation button to dismiss number changed dialog -->
|
||||
<string name="ChangeNumber__okay">Okay</string>
|
||||
|
||||
<!-- ChangeNumberPostRegistrationWaitSheet: title of the sheet shown when the user tries to change number too soon after registering -->
|
||||
<string name="ChangeNumberPostRegistrationWaitSheet__title">Can\'t change number</string>
|
||||
<!-- ChangeNumberPostRegistrationWaitSheet: explanation body shown above the wait-time line -->
|
||||
<string name="ChangeNumberPostRegistrationWaitSheet__body">Because you recently registered this account, you can\'t change phone numbers right now. A short waiting period helps protect your account.</string>
|
||||
<!-- ChangeNumberPostRegistrationWaitSheet: remaining-wait line when wait is one hour or more, %1$d is the number of hours, rounded up -->
|
||||
<plurals name="ChangeNumberPostRegistrationWaitSheet__try_again_in_hours">
|
||||
<item quantity="one">Try again in %1$d hour.</item>
|
||||
<item quantity="other">Try again in %1$d hours.</item>
|
||||
</plurals>
|
||||
<!-- ChangeNumberPostRegistrationWaitSheet: remaining-wait line when wait is under one hour, %1$d is the number of minutes, rounded up -->
|
||||
<plurals name="ChangeNumberPostRegistrationWaitSheet__try_again_in_minutes">
|
||||
<item quantity="one">Try again in %1$d minute.</item>
|
||||
<item quantity="other">Try again in %1$d minutes.</item>
|
||||
</plurals>
|
||||
<!-- ChangeNumberPostRegistrationWaitSheet: dismiss button -->
|
||||
<string name="ChangeNumberPostRegistrationWaitSheet__ok">OK</string>
|
||||
<!-- ChangeNumberEnterCodeFragment: rate-limit dialog message when retry-after is one hour or more, %1$d is hours rounded up -->
|
||||
<plurals name="ChangeNumberEnterCodeFragment__too_many_attempts_try_again_in_hours">
|
||||
<item quantity="one">You have made too many attempts. Please try again in %1$d hour.</item>
|
||||
<item quantity="other">You have made too many attempts. Please try again in %1$d hours.</item>
|
||||
</plurals>
|
||||
<!-- ChangeNumberEnterCodeFragment: rate-limit dialog message when retry-after is under one hour, %1$d is minutes rounded up -->
|
||||
<plurals name="ChangeNumberEnterCodeFragment__too_many_attempts_try_again_in_minutes">
|
||||
<item quantity="one">You have made too many attempts. Please try again in %1$d minute.</item>
|
||||
<item quantity="other">You have made too many attempts. Please try again in %1$d minutes.</item>
|
||||
</plurals>
|
||||
|
||||
<!-- ChangeNumberEnterPhoneNumberFragment -->
|
||||
<string name="ChangeNumberEnterPhoneNumberFragment__change_number">Change Number</string>
|
||||
<string name="ChangeNumberEnterPhoneNumberFragment__your_old_number">Your old number</string>
|
||||
|
||||
Reference in New Issue
Block a user