Xamarin.Android注册流程中实现短信验证码自动填充至EditText的需求
Alright, let's figure out how to implement auto-filling the SMS verification code into your EditText after the user taps "Send SMS" — this will make the flow way smoother for your users. Below are the two most practical approaches, tailored for different Android version targets:
This is the go-to method now because it doesn't require the RECEIVE_SMS permission, making your app more secure and compliant with modern Android policies. Here's how to set it up:
Add the required dependency
In your app-levelbuild.gradle(Module level), add this line to the dependencies block:implementation "com.google.android.gms:play-services-auth-api-phone:18.0.1"(You can check for the latest version in Google's Maven repo if needed.)
Initialize the SMS Retriever client in your Activity
In the Activity where you handle verification, set up the client and a callback to listen for incoming SMS:// Kotlin example val client = SmsRetriever.getClient(this /* context */) val task = client.startSmsRetriever() task.addOnSuccessListener { // Successfully started retriever, wait for SMS Toast.makeText(this, "Waiting for verification SMS...", Toast.LENGTH_SHORT).show() } task.addOnFailureListener { // Failed to start retriever, show error message Toast.makeText(this, "Failed to start SMS retriever", Toast.LENGTH_SHORT).show() }Ensure your SMS follows the required format
The SMS sent from your server must start with<#>and end with your app's unique hash string. For example:<#> Your verification code is 123456 FA+9qCX9VSu
To get your app's hash string, you can use this small code snippet (only needed once to get the hash for your production keystore):
// Kotlin code to generate app hash fun getAppHash(context: Context): String { val packageName = context.packageName val signature = context.packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures[0] val message = signature.toByteArray() + "com.google.android.gms.auth.api.phone.SmsRetrieverApi" val md = MessageDigest.getInstance("SHA-256") md.update(message.toByteArray(Charsets.UTF_8)) return Base64.encodeToString(md.digest(), Base64.NO_WRAP) }Handle the retrieved SMS and fill the code
Create a broadcast receiver to catch the SMS Retriever intent, extract the code, and populate your EditText:class SmsVerificationReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (SmsRetriever.SMS_RETRIEVED_ACTION == intent?.action) { val extras = intent.extras val status = extras?.get(SmsRetriever.EXTRA_STATUS) as Status when (status.statusCode) { CommonStatusCodes.SUCCESS -> { // Get SMS message contents val message = extras.get(SmsRetriever.EXTRA_SMS_MESSAGE) as String // Extract verification code (adjust regex to match your code length) val codePattern = Pattern.compile("\\d{6}") // Matches 6-digit codes val matcher = codePattern.matcher(message) if (matcher.find()) { val verificationCode = matcher.group(0) // Update EditText on UI thread (context as? YourVerificationActivity)?.runOnUiThread { context.yourEditText.setText(verificationCode) // Optional: Auto-trigger the "Verify" button click context.verifyButton.performClick() } } } CommonStatusCodes.TIMEOUT -> { // SMS not received within 5 minutes, show error Toast.makeText(context, "SMS verification timed out", Toast.LENGTH_SHORT).show() } } } } }Don't forget to register this receiver dynamically in your Activity's
onCreatemethod:val receiver = SmsVerificationReceiver() registerReceiver(receiver, IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION))And unregister it in
onDestroy:unregisterReceiver(receiver)
If you need to support older Android versions (before 8.0), you can use a broadcast receiver, but note that this requires the RECEIVE_SMS permission and has restrictions on Android 10+.
Declare permissions
Add this to yourAndroidManifest.xml:<uses-permission android:name="android.permission.RECEIVE_SMS" />For Android 6.0+, you'll need to request this permission dynamically from the user before registering the receiver.
Create the SMS Broadcast Receiver
class SmsReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION == intent?.action) { val smsMessages = Telephony.Sms.Intents.getMessagesFromIntent(intent) for (message in smsMessages) { val smsBody = message.messageBody // Extract verification code val codePattern = Pattern.compile("\\d{6}") val matcher = codePattern.matcher(smsBody) if (matcher.find()) { val code = matcher.group(0) (context as? YourVerificationActivity)?.runOnUiThread { context.yourEditText.setText(code) // Auto-click verify button if needed context.verifyButton.performClick() } abortBroadcast() // Prevent other apps from receiving this SMS break } } } } }Register the receiver dynamically
Dynamic registration is better than static to avoid being blocked by Android's background restrictions:if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_SMS) == PackageManager.PERMISSION_GRANTED) { val smsReceiver = SmsReceiver() registerReceiver(smsReceiver, IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)) } else { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.RECEIVE_SMS), REQUEST_CODE_SMS_PERMISSION) }
Pro Tips:
- Adjust the regex pattern (
\\d{6}) to match your verification code's length (e.g.,\\d{4}for 4-digit codes). - For the SMS Retriever API, make sure your server sends the exact hash string — using the wrong hash will prevent the API from detecting the SMS.
- Always handle cases where the user doesn't receive the SMS (timeouts, permission denials) with clear error messages.
内容的提问来源于stack exchange,提问作者NaSt

