Last active
July 11, 2022 20:00
-
-
Save azlekov/6e83e570525ee738e8b7cd28967e5cf8 to your computer and use it in GitHub Desktop.
Jetpack Compose Phone Auth
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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()) | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | |
) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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