Skip to content

Instantly share code, notes, and snippets.

@azlekov
Last active July 11, 2022 20:00
Show Gist options
  • Save azlekov/6e83e570525ee738e8b7cd28967e5cf8 to your computer and use it in GitHub Desktop.
Save azlekov/6e83e570525ee738e8b7cd28967e5cf8 to your computer and use it in GitHub Desktop.
Jetpack Compose Phone Auth
dependencies {
implementation("com.google.android.gms:play-services-auth:20.2.0")
implementation("com.google.android.gms:play-services-auth-api-phone:18.0.1")
implementation("com.googlecode.libphonenumber:libphonenumber:8.12.48")
}
@Composable
fun PhoneNumberConsent(
onPhoneNumberFetchedFromDevice: (phoneNumber: String) -> Unit,
) {
val context = LocalContext.current
val phoneNumberHintIntentResultLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
try {
val phoneNumber = Identity.getSignInClient(context).getPhoneNumberFromIntent(result.data)
onPhoneNumberFetchedFromDevice(phoneNumber)
} catch(e: Exception) {
Log.e("PhoneAuth", "Phone Number Hint failed")
}
}
LaunchedEffect(Unit) {
val request: GetPhoneNumberHintIntentRequest =
GetPhoneNumberHintIntentRequest.builder().build()
Identity.getSignInClient(context)
.getPhoneNumberHintIntent(request)
.addOnSuccessListener {
try {
phoneNumberHintIntentResultLauncher.launch(IntentSenderRequest.Builder(it).build())
} catch(e: Exception) {
Log.e("PhoneAuth", "Launching the PendingIntent failed")
}
}.addOnFailureListener {
Log.e("PhoneAuth", it.toString())
}
}
}
class PhoneNumberVisualTransformation(
countryCode: String = Locale.current.region
) : VisualTransformation {
private val phoneNumberFormatter =
PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode)
override fun filter(text: AnnotatedString): TransformedText {
val transformation = reformat(text, Selection.getSelectionEnd(text))
return TransformedText(AnnotatedString(transformation.formatted ?: ""), object :
OffsetMapping {
override fun originalToTransformed(offset: Int): Int {
return transformation.originalToTransformed[offset]
}
override fun transformedToOriginal(offset: Int): Int {
return transformation.transformedToOriginal[offset]
}
})
}
private fun reformat(s: CharSequence, cursor: Int): Transformation {
phoneNumberFormatter.clear()
val curIndex = cursor - 1
var formatted: String? = null
var lastNonSeparator = 0.toChar()
var hasCursor = false
s.forEachIndexed { index, char ->
if (PhoneNumberUtils.isNonSeparator(char)) {
if (lastNonSeparator.code != 0) {
formatted = getFormattedNumber(lastNonSeparator, hasCursor)
hasCursor = false
}
lastNonSeparator = char
}
if (index == curIndex) {
hasCursor = true
}
}
if (lastNonSeparator.code != 0) {
formatted = getFormattedNumber(lastNonSeparator, hasCursor)
}
val originalToTransformed = mutableListOf<Int>()
val transformedToOriginal = mutableListOf<Int>()
var specialCharsCount = 0
formatted?.forEachIndexed { index, char ->
if (!PhoneNumberUtils.isNonSeparator(char)) {
specialCharsCount++
} else {
originalToTransformed.add(index)
}
transformedToOriginal.add(index - specialCharsCount)
}
originalToTransformed.add(originalToTransformed.maxOrNull()?.plus(1) ?: 0)
transformedToOriginal.add(transformedToOriginal.maxOrNull()?.plus(1) ?: 0)
return Transformation(formatted, originalToTransformed, transformedToOriginal)
}
private fun getFormattedNumber(lastNonSeparator: Char, hasCursor: Boolean): String? {
return if (hasCursor) {
phoneNumberFormatter.inputDigitAndRememberPosition(lastNonSeparator)
} else {
phoneNumberFormatter.inputDigit(lastNonSeparator)
}
}
private data class Transformation(
val formatted: String?,
val originalToTransformed: List<Int>,
val transformedToOriginal: List<Int>
)
}
@Composable
fun SignInScreen() {
var text by remember { mutableStateOf(TextFieldValue("")) }
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
.background(Color.White)
) {
TextField(
value = text,
onValueChange = { text = it },
modifier = Modifier.fillMaxWidth(),
placeholder = { Text(text = "+359 888 123456")},
singleLine = true,
leadingIcon = null,
visualTransformation = PhoneNumberVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone),
)
}
}
@Preview(showBackground = true, device = Devices.PIXEL_2)
@Composable
fun DefaultSignInScreen() {
DodiTheme {
SignInScreen()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment