mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-26 22:20:20 +00:00
Add polish to usernames UX.
This commit is contained in:
committed by
Nicholas Tinsley
parent
ec96b4e3aa
commit
38d5d3ad1b
@@ -42,6 +42,7 @@ import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
import org.thoughtcrime.securesms.util.views.Stub
|
||||
import org.thoughtcrime.securesms.util.visible
|
||||
|
||||
class AppSettingsFragment : DSLSettingsFragment(
|
||||
titleId = R.string.text_secure_normal__menu_settings,
|
||||
@@ -340,6 +341,7 @@ class AppSettingsFragment : DSLSettingsFragment(
|
||||
private val aboutView: EmojiTextView = itemView.findViewById(R.id.about)
|
||||
private val badgeView: BadgeImageView = itemView.findViewById(R.id.badge)
|
||||
private val qrButton: View = itemView.findViewById(R.id.qr_button)
|
||||
private val usernameView: TextView = itemView.findViewById(R.id.username)
|
||||
|
||||
init {
|
||||
aboutView.setOverflowText(" ")
|
||||
@@ -352,6 +354,8 @@ class AppSettingsFragment : DSLSettingsFragment(
|
||||
|
||||
titleView.text = model.recipient.profileName.toString()
|
||||
summaryView.text = PhoneNumberFormatter.prettyPrint(model.recipient.requireE164())
|
||||
usernameView.text = model.recipient.username.orElse("")
|
||||
usernameView.visible = model.recipient.username.isPresent
|
||||
avatarView.setRecipient(Recipient.self())
|
||||
badgeView.setBadgeFromRecipient(Recipient.self())
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import androidx.compose.ui.graphics.Color
|
||||
enum class UsernameQrCodeColorScheme(
|
||||
val borderColor: Color,
|
||||
val foregroundColor: Color,
|
||||
val backgroundColor: Color,
|
||||
val textColor: Color = Color.White,
|
||||
val outlineColor: Color = Color.Transparent,
|
||||
private val key: String
|
||||
@@ -15,11 +16,13 @@ enum class UsernameQrCodeColorScheme(
|
||||
Blue(
|
||||
borderColor = Color(0xFF506ECD),
|
||||
foregroundColor = Color(0xFF2449C0),
|
||||
backgroundColor = Color(0xFFEDF0FA),
|
||||
key = "blue"
|
||||
),
|
||||
White(
|
||||
borderColor = Color(0xFFFFFFFF),
|
||||
foregroundColor = Color(0xFF000000),
|
||||
backgroundColor = Color(0xFFF5F5F5),
|
||||
textColor = Color.Black,
|
||||
outlineColor = Color(0xFFE9E9E9),
|
||||
key = "white"
|
||||
@@ -27,31 +30,37 @@ enum class UsernameQrCodeColorScheme(
|
||||
Grey(
|
||||
borderColor = Color(0xFF6A6C74),
|
||||
foregroundColor = Color(0xFF464852),
|
||||
backgroundColor = Color(0xFFF0F0F1),
|
||||
key = "grey"
|
||||
),
|
||||
Tan(
|
||||
borderColor = Color(0xFFBBB29A),
|
||||
foregroundColor = Color(0xFF73694F),
|
||||
backgroundColor = Color(0xFFF6F5F2),
|
||||
key = "tan"
|
||||
),
|
||||
Green(
|
||||
borderColor = Color(0xFF97AA89),
|
||||
foregroundColor = Color(0xFF55733F),
|
||||
backgroundColor = Color(0xFFF2F5F0),
|
||||
key = "green"
|
||||
),
|
||||
Orange(
|
||||
borderColor = Color(0xFFDE7134),
|
||||
foregroundColor = Color(0xFFDA6C2E),
|
||||
backgroundColor = Color(0xFFFCF1EB),
|
||||
key = "orange"
|
||||
),
|
||||
Pink(
|
||||
borderColor = Color(0xFFEA7B9D),
|
||||
foregroundColor = Color(0xFFBB617B),
|
||||
backgroundColor = Color(0xFFFCF1F5),
|
||||
key = "pink"
|
||||
),
|
||||
Purple(
|
||||
borderColor = Color(0xFF9E7BE9),
|
||||
foregroundColor = Color(0xFF7651C5),
|
||||
backgroundColor = Color(0xFFF5F3FA),
|
||||
key = "purple"
|
||||
);
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
@@ -56,6 +55,7 @@ import com.google.accompanist.permissions.rememberPermissionState
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.signal.core.ui.Buttons
|
||||
import org.signal.core.ui.Dialogs
|
||||
import org.signal.core.ui.Snackbars
|
||||
import org.signal.core.ui.theme.SignalTheme
|
||||
import org.signal.core.util.concurrent.LifecycleDisposable
|
||||
import org.thoughtcrime.securesms.R
|
||||
@@ -89,7 +89,9 @@ class UsernameLinkSettingsFragment : ComposeFragment() {
|
||||
val navController: NavController by remember { mutableStateOf(findNavController()) }
|
||||
var showResetDialog: Boolean by remember { mutableStateOf(false) }
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
val cameraPermissionState: PermissionState = rememberPermissionState(permission = android.Manifest.permission.CAMERA)
|
||||
val cameraPermissionState: PermissionState = rememberPermissionState(permission = android.Manifest.permission.CAMERA) {
|
||||
viewModel.onTabSelected(ActiveTab.Scan)
|
||||
}
|
||||
val linkCopiedEvent: UUID? by viewModel.linkCopiedEvent
|
||||
|
||||
val linkCopiedString = stringResource(R.string.UsernameLinkSettings_link_copied_toast)
|
||||
@@ -101,7 +103,7 @@ class UsernameLinkSettingsFragment : ComposeFragment() {
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
snackbarHost = { Snackbars.Host(snackbarHostState) },
|
||||
topBar = {
|
||||
TopAppBarContent(
|
||||
activeTab = state.activeTab,
|
||||
@@ -123,6 +125,7 @@ class UsernameLinkSettingsFragment : ComposeFragment() {
|
||||
enter = slideInHorizontally(initialOffsetX = { fullWidth -> -fullWidth }),
|
||||
exit = slideOutHorizontally(targetOffsetX = { fullWidth -> -fullWidth })
|
||||
) {
|
||||
val helpText = stringResource(id = R.string.UsernameLinkSettings_scan_this_qr_code)
|
||||
UsernameLinkShareScreen(
|
||||
state = state,
|
||||
snackbarHostState = snackbarHostState,
|
||||
@@ -130,7 +133,7 @@ class UsernameLinkSettingsFragment : ComposeFragment() {
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
navController = navController,
|
||||
onShareBadge = {
|
||||
shareQrBadge(viewModel.generateQrCodeImage())
|
||||
shareQrBadge(viewModel.generateQrCodeImage(helpText))
|
||||
},
|
||||
onResetClicked = { showResetDialog = true },
|
||||
onLinkResultHandled = { viewModel.onUsernameLinkResetResultHandled() }
|
||||
|
||||
@@ -10,6 +10,9 @@ import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build
|
||||
import android.text.Layout
|
||||
import android.text.StaticLayout
|
||||
import android.text.TextPaint
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -17,8 +20,10 @@ import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Canvas
|
||||
import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.graphics.withSave
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.core.graphics.withTranslation
|
||||
import androidx.lifecycle.ViewModel
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
@@ -122,7 +127,9 @@ class UsernameLinkSettingsViewModel : ViewModel() {
|
||||
val components: Optional<UsernameLinkComponents> = when (result) {
|
||||
is UsernameLinkResetResult.Success -> Optional.of(result.components)
|
||||
is UsernameLinkResetResult.NetworkError -> Optional.empty()
|
||||
else -> { usernameLink.value ?: Optional.empty() }
|
||||
else -> {
|
||||
usernameLink.value ?: Optional.empty()
|
||||
}
|
||||
}
|
||||
|
||||
_state.value = _state.value.copy(
|
||||
@@ -200,7 +207,7 @@ class UsernameLinkSettingsViewModel : ViewModel() {
|
||||
*
|
||||
* I hate this as much as you do.
|
||||
*/
|
||||
fun generateQrCodeImage(): Bitmap? {
|
||||
fun generateQrCodeImage(helpText: String): Bitmap? {
|
||||
val state: UsernameLinkSettingsState = _state.value
|
||||
|
||||
if (state.qrCodeState !is QrCodeState.Present) {
|
||||
@@ -210,12 +217,16 @@ class UsernameLinkSettingsViewModel : ViewModel() {
|
||||
|
||||
val qrCodeData: QrCodeData = state.qrCodeState.data
|
||||
|
||||
val width = 480
|
||||
val height = 525
|
||||
val qrSize = 300f
|
||||
val qrPadding = 25f
|
||||
val borderSizeX = 64f
|
||||
val borderSizeY = 52f
|
||||
val width = 424
|
||||
val height = 576
|
||||
val backgroundPadHorizontal = 64f
|
||||
val backgroundPadVertical = 80f
|
||||
val qrBorderWidth = width - (backgroundPadHorizontal * 2)
|
||||
val qrBorderHeight = 324f
|
||||
val qrSize = 184f
|
||||
val qrPadding = 16f
|
||||
val borderSizeX = 40f
|
||||
val borderSizeY = 32f
|
||||
|
||||
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply {
|
||||
eraseColor(Color.TRANSPARENT)
|
||||
@@ -225,56 +236,70 @@ class UsernameLinkSettingsViewModel : ViewModel() {
|
||||
val composeCanvas = Canvas(androidCanvas)
|
||||
val canvasDrawScope = CanvasDrawScope()
|
||||
|
||||
// Draw the background
|
||||
androidCanvas.drawRoundRect(0f, 0f, width.toFloat(), height.toFloat(), 30f, 30f, Paint().apply { color = state.qrCodeColorScheme.borderColor.toArgb() })
|
||||
androidCanvas.drawRoundRect(borderSizeX, borderSizeY, borderSizeX + qrSize + qrPadding * 2, borderSizeY + qrSize + qrPadding * 2, 15f, 15f, Paint().apply { color = Color.WHITE })
|
||||
androidCanvas.drawRoundRect(
|
||||
borderSizeX,
|
||||
borderSizeY,
|
||||
borderSizeX + qrSize + qrPadding * 2,
|
||||
borderSizeY + qrSize + qrPadding * 2,
|
||||
15f,
|
||||
15f,
|
||||
Paint().apply {
|
||||
color = state.qrCodeColorScheme.outlineColor.toArgb()
|
||||
style = Paint.Style.STROKE
|
||||
strokeWidth = 4f
|
||||
}
|
||||
)
|
||||
// Background
|
||||
androidCanvas.drawColor(state.qrCodeColorScheme.backgroundColor.toArgb())
|
||||
|
||||
// Draw the QR code
|
||||
composeCanvas.translate((width / 2) - (qrSize / 2), 80f)
|
||||
canvasDrawScope.draw(
|
||||
density = object : Density {
|
||||
override val density: Float = 1f
|
||||
override val fontScale: Float = 1f
|
||||
},
|
||||
layoutDirection = LayoutDirection.Ltr,
|
||||
canvas = composeCanvas,
|
||||
size = Size(qrSize, qrSize)
|
||||
) {
|
||||
drawQr(
|
||||
data = qrCodeData,
|
||||
foregroundColor = state.qrCodeColorScheme.foregroundColor,
|
||||
backgroundColor = state.qrCodeColorScheme.borderColor,
|
||||
deadzonePercent = 0.35f,
|
||||
logo = null
|
||||
// QR Border
|
||||
androidCanvas.withTranslation(x = backgroundPadHorizontal, y = backgroundPadVertical) {
|
||||
drawRoundRect(0f, 0f, qrBorderWidth, qrBorderHeight, 30f, 30f, Paint().apply { color = state.qrCodeColorScheme.borderColor.toArgb() })
|
||||
|
||||
drawRoundRect(borderSizeX, borderSizeY, borderSizeX + qrSize + qrPadding * 2, borderSizeY + qrSize + qrPadding * 2, 15f, 15f, Paint().apply { color = Color.WHITE })
|
||||
drawRoundRect(
|
||||
borderSizeX,
|
||||
borderSizeY,
|
||||
borderSizeX + qrSize + qrPadding * 2,
|
||||
borderSizeY + qrSize + qrPadding * 2,
|
||||
15f,
|
||||
15f,
|
||||
Paint().apply {
|
||||
color = state.qrCodeColorScheme.outlineColor.toArgb()
|
||||
style = Paint.Style.STROKE
|
||||
strokeWidth = 4f
|
||||
}
|
||||
)
|
||||
}
|
||||
composeCanvas.translate(-90f, -80f)
|
||||
|
||||
// Draw the signal logo -- unfortunately can't have the normal QR code drawing handle it because it requires a composable ImageBitmap
|
||||
BitmapFactory.decodeResource(ApplicationDependencies.getApplication().resources, R.drawable.qrcode_logo).also { logoBitmap ->
|
||||
val tintedPaint = Paint().apply {
|
||||
colorFilter = PorterDuffColorFilter(state.qrCodeColorScheme.foregroundColor.toArgb(), PorterDuff.Mode.SRC_IN)
|
||||
// Draw the QR code
|
||||
composeCanvas.translate((qrBorderWidth / 2) - (qrSize / 2), borderSizeY + qrPadding)
|
||||
|
||||
composeCanvas.withSave {
|
||||
composeCanvas.scale(qrSize / 300f, qrSize / 300f)
|
||||
canvasDrawScope.draw(
|
||||
density = object : Density {
|
||||
override val density: Float = 1f
|
||||
override val fontScale: Float = 1f
|
||||
},
|
||||
layoutDirection = LayoutDirection.Ltr,
|
||||
canvas = composeCanvas,
|
||||
size = Size(300f, 300f)
|
||||
) {
|
||||
drawQr(
|
||||
data = qrCodeData,
|
||||
foregroundColor = state.qrCodeColorScheme.foregroundColor,
|
||||
backgroundColor = state.qrCodeColorScheme.borderColor,
|
||||
deadzonePercent = 0.35f,
|
||||
logo = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
composeCanvas.translate(-90f, -80f)
|
||||
|
||||
// Draw the signal logo -- unfortunately can't have the normal QR code drawing handle it because it requires a composable ImageBitmap
|
||||
BitmapFactory.decodeResource(ApplicationDependencies.getApplication().resources, R.drawable.qrcode_logo).also { logoBitmap ->
|
||||
val tintedPaint = Paint().apply {
|
||||
colorFilter = PorterDuffColorFilter(state.qrCodeColorScheme.foregroundColor.toArgb(), PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
val sourceRect = Rect(0, 0, logoBitmap.width, logoBitmap.height)
|
||||
|
||||
val destLeft = qrBorderWidth / 2f + qrPadding
|
||||
val destTop = destLeft - 10f
|
||||
val destRect = RectF(destLeft, destTop, destLeft + 36f, destTop + 36f)
|
||||
drawBitmap(logoBitmap, sourceRect, destRect, tintedPaint)
|
||||
}
|
||||
val sourceRect = Rect(0, 0, logoBitmap.width, logoBitmap.height)
|
||||
val destRect = RectF(210f, 200f, 270f, 260f)
|
||||
androidCanvas.drawBitmap(logoBitmap, sourceRect, destRect, tintedPaint)
|
||||
}
|
||||
|
||||
// Draw the text
|
||||
val textPaint = Paint().apply {
|
||||
// Draw the username
|
||||
val usernamePaint = Paint().apply {
|
||||
color = state.qrCodeColorScheme.textColor.toArgb()
|
||||
textSize = 34f
|
||||
typeface = if (Build.VERSION.SDK_INT < 26) {
|
||||
@@ -286,10 +311,33 @@ class UsernameLinkSettingsViewModel : ViewModel() {
|
||||
.build()
|
||||
}
|
||||
}
|
||||
val textBounds = Rect()
|
||||
textPaint.getTextBounds(state.username, 0, state.username.length, textBounds)
|
||||
val usernameBounds = Rect()
|
||||
usernamePaint.getTextBounds(state.username, 0, state.username.length, usernameBounds)
|
||||
|
||||
androidCanvas.drawText(state.username, (width / 2f) - (textBounds.width() / 2f), 465f, textPaint)
|
||||
androidCanvas.drawText(state.username, (width / 2f) - (usernameBounds.width() / 2f), 348f + usernameBounds.height(), usernamePaint)
|
||||
|
||||
// Draw the help text
|
||||
val helpTextPaint = TextPaint().apply {
|
||||
isAntiAlias = true
|
||||
color = 0xFF3C3C43.toInt()
|
||||
textSize = 14f
|
||||
typeface = if (Build.VERSION.SDK_INT < 26) {
|
||||
Typeface.DEFAULT
|
||||
} else {
|
||||
Typeface.Builder("")
|
||||
.setFallback("sans-serif")
|
||||
.setWeight(400)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
val helpTextHorizontalPad = 72
|
||||
val maxWidth = width - helpTextHorizontalPad * 2
|
||||
val helpTextLayout = StaticLayout(helpText, helpTextPaint, maxWidth, Layout.Alignment.ALIGN_CENTER, 1f, 0f, true)
|
||||
|
||||
androidCanvas.withTranslation(x = helpTextHorizontalPad.toFloat(), y = 444f) {
|
||||
helpTextLayout.draw(androidCanvas)
|
||||
}
|
||||
|
||||
return bitmap
|
||||
}
|
||||
|
||||
@@ -93,21 +93,18 @@ fun UsernameLinkShareScreen(
|
||||
|
||||
ButtonBar(
|
||||
onShareClicked = onShareBadge,
|
||||
onColorClicked = { navController.safeNavigate(UsernameLinkSettingsFragmentDirections.actionUsernameLinkSettingsFragmentToUsernameLinkQrColorPickerFragment()) }
|
||||
)
|
||||
|
||||
LinkRow(
|
||||
linkState = state.usernameLinkState,
|
||||
onClick = {
|
||||
onColorClicked = { navController.safeNavigate(UsernameLinkSettingsFragmentDirections.actionUsernameLinkSettingsFragmentToUsernameLinkQrColorPickerFragment()) },
|
||||
onLinkClicked = {
|
||||
navController.safeNavigate(UsernameLinkSettingsFragmentDirections.actionUsernameLinkSettingsFragmentToUsernameLinkShareBottomSheet())
|
||||
}
|
||||
},
|
||||
linkState = state.usernameLinkState
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.UsernameLinkSettings_qr_description),
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(bottom = 19.dp, start = 43.dp, end = 43.dp),
|
||||
modifier = Modifier.padding(top = 42.dp, bottom = 19.dp, start = 43.dp, end = 43.dp),
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
|
||||
@@ -127,11 +124,22 @@ fun UsernameLinkShareScreen(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ButtonBar(onShareClicked: () -> Unit, onColorClicked: () -> Unit) {
|
||||
private fun ButtonBar(
|
||||
linkState: UsernameLinkState,
|
||||
onLinkClicked: () -> Unit,
|
||||
onShareClicked: () -> Unit,
|
||||
onColorClicked: () -> Unit
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 32.dp, alignment = Alignment.CenterHorizontally),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Buttons.ActionButton(
|
||||
enabled = linkState is UsernameLinkState.Present,
|
||||
onClick = onLinkClicked,
|
||||
iconResId = R.drawable.symbol_link_24,
|
||||
labelResId = R.string.UsernameLinkSettings_link_button_label
|
||||
)
|
||||
Buttons.ActionButton(
|
||||
onClick = onShareClicked,
|
||||
iconResId = R.drawable.symbol_share_android_24,
|
||||
|
||||
@@ -194,6 +194,8 @@ public class UsernameEditFragment extends LoggingFragment {
|
||||
case DISCRIMINATOR_HAS_INVALID_CHARACTERS, DISCRIMINATOR_NOT_AVAILABLE -> getString(R.string.UsernameEditFragment__this_username_is_not_available_try_another_number);
|
||||
case DISCRIMINATOR_TOO_LONG -> getString(R.string.UsernameEditFragment__invalid_username_enter_a_maximum_of_d_digits, UsernameUtil.MAX_DISCRIMINATOR_LENGTH);
|
||||
case DISCRIMINATOR_TOO_SHORT -> getString(R.string.UsernameEditFragment__invalid_username_enter_a_minimum_of_d_digits, UsernameUtil.MIN_DISCRIMINATOR_LENGTH);
|
||||
case DISCRIMINATOR_CANNOT_BE_00 -> getString(R.string.UsernameEditFragment__this_number_cant_be_00);
|
||||
case DISCRIMINATOR_CANNOT_START_WITH_00 -> getString(R.string.UsernameEditFragment__this_number_cant_start_with_00);
|
||||
};
|
||||
|
||||
int colorRes = error != null ? R.color.signal_colorError : R.color.signal_colorPrimary;
|
||||
|
||||
@@ -355,7 +355,9 @@ internal class UsernameEditViewModel private constructor(private val mode: Usern
|
||||
DISCRIMINATOR_NOT_AVAILABLE,
|
||||
DISCRIMINATOR_TOO_SHORT,
|
||||
DISCRIMINATOR_TOO_LONG,
|
||||
DISCRIMINATOR_HAS_INVALID_CHARACTERS
|
||||
DISCRIMINATOR_HAS_INVALID_CHARACTERS,
|
||||
DISCRIMINATOR_CANNOT_BE_00,
|
||||
DISCRIMINATOR_CANNOT_START_WITH_00
|
||||
}
|
||||
|
||||
enum class ButtonState {
|
||||
@@ -383,6 +385,7 @@ internal class UsernameEditViewModel private constructor(private val mode: Usern
|
||||
InvalidReason.TOO_LONG -> UsernameStatus.TOO_LONG
|
||||
InvalidReason.STARTS_WITH_NUMBER -> UsernameStatus.CANNOT_START_WITH_NUMBER
|
||||
InvalidReason.INVALID_CHARACTERS -> UsernameStatus.INVALID_CHARACTERS
|
||||
InvalidReason.INVALID_NUMBER, InvalidReason.INVALID_NUMBER_PREFIX -> error("Unexpected reason $invalidReason")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,6 +394,8 @@ internal class UsernameEditViewModel private constructor(private val mode: Usern
|
||||
InvalidReason.TOO_SHORT -> UsernameStatus.DISCRIMINATOR_TOO_SHORT
|
||||
InvalidReason.TOO_LONG -> UsernameStatus.DISCRIMINATOR_TOO_LONG
|
||||
InvalidReason.INVALID_CHARACTERS -> UsernameStatus.DISCRIMINATOR_HAS_INVALID_CHARACTERS
|
||||
InvalidReason.INVALID_NUMBER -> UsernameStatus.DISCRIMINATOR_CANNOT_BE_00
|
||||
InvalidReason.INVALID_NUMBER_PREFIX -> UsernameStatus.DISCRIMINATOR_CANNOT_START_WITH_00
|
||||
else -> UsernameStatus.INVALID_GENERIC
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ public final class SpanUtil {
|
||||
|
||||
public static void appendBottomImageSpan(@NonNull SpannableStringBuilder builder, @NonNull Drawable drawable, int width, int height) {
|
||||
drawable.setBounds(0, 0, ViewUtil.dpToPx(width), ViewUtil.dpToPx(height));
|
||||
builder.append(" ").append(SpanUtil.buildImageSpan(drawable, DynamicDrawableSpan.ALIGN_BOTTOM));
|
||||
builder.append(SpanUtil.buildImageSpan(drawable, DynamicDrawableSpan.ALIGN_BOTTOM));
|
||||
}
|
||||
|
||||
public static CharSequence learnMore(@NonNull Context context,
|
||||
|
||||
@@ -64,6 +64,12 @@ object UsernameUtil {
|
||||
value == null -> {
|
||||
null
|
||||
}
|
||||
value == "00" -> {
|
||||
InvalidReason.INVALID_NUMBER
|
||||
}
|
||||
value.startsWith("00") -> {
|
||||
InvalidReason.INVALID_NUMBER_PREFIX
|
||||
}
|
||||
value.length < MIN_DISCRIMINATOR_LENGTH -> {
|
||||
InvalidReason.TOO_SHORT
|
||||
}
|
||||
@@ -83,6 +89,8 @@ object UsernameUtil {
|
||||
TOO_SHORT,
|
||||
TOO_LONG,
|
||||
INVALID_CHARACTERS,
|
||||
STARTS_WITH_NUMBER
|
||||
STARTS_WITH_NUMBER,
|
||||
INVALID_NUMBER,
|
||||
INVALID_NUMBER_PREFIX
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user