Unresolved reference: getIntentSender / While trying to obtain phone number in Jetpack Compose - android

I am trying to obtain phone number(s) in Jetpack compose following Googles Phone Number Hint Docs. But I am stuck in a problem where it says: getIntentSender() is unresolved in request: GetPhoneNumberHintIntentRequest.
I am also getting another error on addOnFailureListener
Type mismatch.
Required:
OnFailureListener
Found:
Int
#Composable
fun PhoneNumberConsent() {
val context = LocalContext.current
val request = GetPhoneNumberHintIntentRequest.builder().build()
val phoneNumberHintIntentResultLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
) {
try {
val phoneNumber =
Identity.getSignInClient(context)
.getPhoneNumberFromIntent(it.data)
} catch (e: Exception) {
Log.e(TAG, "Phone Number Hint failed")
}
}
Identity.getSignInClient(context)
.getPhoneNumberHintIntent(request)
.addOnSuccessListener(
try {
phoneNumberHintIntentResultLauncher.launch(request.getIntentSender())
} catch (e: Exception) {
Log.e(TAG, "Launching the PendingIntent failed")
} as OnSuccessListener<in PendingIntent>
)
.addOnFailureListener(
Log.e(TAG, "Phone Number Hint failed")
)
}

addOnSuccessListener accepts a listener, which can be passed as trailing closure.
Result passed to this listener is a pending intent which has intentSender property, and it can be used to create IntentSenderRequest.
Here's a working example:
val context = LocalContext.current
val request = GetPhoneNumberHintIntentRequest.builder().build()
val phoneNumberHintIntentResultLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartIntentSenderForResult(),
) {
try {
val phoneNumber = Identity.getSignInClient(context)
.getPhoneNumberFromIntent(it.data)
println("phoneNumber $phoneNumber")
} catch (e: Exception) {
println("Phone Number Hint failed")
e.printStackTrace()
}
}
Button(onClick = {
Identity.getSignInClient(context)
.getPhoneNumberHintIntent(request)
.addOnSuccessListener { pendingIntent ->
try {
phoneNumberHintIntentResultLauncher.launch(
IntentSenderRequest.Builder(
pendingIntent.intentSender
).build()
)
} catch (e: Exception) {
println("Launching the PendingIntent failed")
e.printStackTrace()
}
}
.addOnFailureListener {
println("addOnFailureListener $it")
}
}) {
}
If you need to run it immediately after the view appears, use LaunchedEffect instead of Button.onClick. Your current approach contradicts one of the basic rules of Compose, which is that composable functions must be free of side-effects. Read more in thinking in compose

According to this docs there is no getIntentSender() method in GetPhoneNumberHintIntentRequest class. Maybe there is a typo in the tutorial you are following, try to use result instead of request:
Identity.getSignInClient(context)
.getPhoneNumberHintIntent(request)
.addOnSuccessListener { result ->
try {
phoneNumberHintIntentResultLauncher.launch(result.intentSender.sendIntent)
} catch (e: Exception) {
Log.e(TAG, "Launching the PendingIntent failed")
} as OnSuccessListener<in PendingIntent>
}
.addOnFailureListener(
Log.e(TAG, "Phone Number Hint failed")
)

Related

How can I open gmail when click the button in jetpack compose?

I have simple button in android jetpack compose, when I click the button, I want to open gmail and send mail to "android#gmail.com", is it possible?
#Composable
fun SimpleButton() {
Button(onClick = {
//your onclick code here
}) {
Text(text = "Simple Button")
}
}
You have to create an Intent and then start an Activity with it, similar to how you would have to do it normally.
The only difference in Compose is that you obtain the Context with LocalContext.current.
#Composable
fun SimpleButton() {
val context = LocalContext.current
Column {
Button(onClick = {
context.sendMail(to = "example#gmail.com", subject = "Some subject")
}) {
Text(text = "Send mail")
}
Button(onClick = {
context.dial(phone = "12345678")
}) {
Text(text = "Dial number")
}
}
}
fun Context.sendMail(to: String, subject: String) {
try {
val intent = Intent(Intent.ACTION_SEND)
intent.type = "vnd.android.cursor.item/email" // or "message/rfc822"
intent.putExtra(Intent.EXTRA_EMAIL, arrayOf(to))
intent.putExtra(Intent.EXTRA_SUBJECT, subject)
startActivity(intent)
} catch (e: ActivityNotFoundException) {
// TODO: Handle case where no email app is available
} catch (t: Throwable) {
// TODO: Handle potential other type of exceptions
}
}
fun Context.dial(phone: String) {
try {
val intent = Intent(Intent.ACTION_DIAL, Uri.fromParts("tel", phone, null))
startActivity(intent)
} catch (t: Throwable) {
// TODO: Handle potential exceptions
}
}
For more possibilities see answers here, but keep in mind that some are outdated.

Get user number using intent request not working?

I am justing trying to get phone number using GetPhoneNumberHintIntentRequest to replace HintRequest.
So just trying to follow google developer doc https://developers.google.com/identity/phone-number-hint/android#kotlin_2. But after following doc I feel this doc is incomplete.
val phoneNumberHintIntentResultLauncher: ActivityResultLauncher<Intent> =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
try {
val phoneNumber = Identity.getSignInClient(requireActivity()).getPhoneNumberFromIntent(result.data)
} catch(e: Exception) {
}
}
So as per doc you need to pass intent to phoneNumberHintIntentResultLauncher but there is no method inside GetPhoneNumberHintIntentRequest.
Even if you see doc then you realise that you need to replace signInClient to getSignInClient.
If any one know about above issue then let me know or any doc where I can achieve my goal.
Have been facing this recently.
Please change the result launcher type as follows.
val resultLauncher: ActivityResultLauncher<IntentSenderRequest> = registerForActivityResult(StartIntentSenderForResult()) { result ->
try {
val phoneNumber = Identity.getSignInClient(requireActivity()).getPhoneNumberFromIntent(result.data)
// Do something with the number
} catch (e: Exception) {
Log.e(TAG, "Phone Number Hint failed")
}
And launch the intent as
...
.addOnSuccessListener { request: PendingIntent ->
try {
resultLauncher.launch(IntentSenderRequest.Builder(request).build())
} catch(e: Exception) {
Log.e(TAG, "Launching the PendingIntent failed")
}
}
...
The document is indeed incomplete as it seems.
// initialise above onCreate
private ActivityResultLauncher<IntentSenderRequest> someActivityResultLauncher;
// declare in onCreate
private void phoneSelection() {
GetPhoneNumberHintIntentRequest request = GetPhoneNumberHintIntentRequest.builder().build();
Identity.getSignInClient(RegistrationActivity.this)
.getPhoneNumberHintIntent(request)
.addOnFailureListener(e -> {
Toast.makeText(activity, "Error : "+e.getMessage(), Toast.LENGTH_SHORT).show();
}).addOnSuccessListener(pendingIntent -> {
IntentSenderRequest intentSenderRequest = new IntentSenderRequest.Builder(pendingIntent.getIntentSender()).build();
someActivityResultLauncher.launch(intentSenderRequest);
});
}
// declare in onCreate
private void resultLauncher() {
someActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartIntentSenderForResult(), result -> {
try {
String phoneNumber = Identity.getSignInClient(RegistrationActivity.this).getPhoneNumberFromIntent(result.getData());
binding.editNumber.setText(phoneNumber.substring(3)); //get the selected phone
} catch (ApiException e) {
e.printStackTrace();
Toast.makeText(activity, "Error : "+ e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}

Not Getting result from rememberLauncherForActivityResult() in jetpack compose

I'm calling StartIntentSenderForResult() but it doesn't get called.
val authResult = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartIntentSenderForResult()
) {
Log.d("appDebug", "called!!!") // not get called
}
oneTapClient.beginSignIn(signUpRequest)
.addOnSuccessListener(activity) { result ->
try {
// Calling here for result
authResult.launch(
IntentSenderRequest
.Builder(result.pendingIntent.intentSender)
.build()
)
} catch (e: IntentSender.SendIntentException) {
Log.d("appDebug", "CATCH : ${e.localizedMessage}")
}
}
.addOnFailureListener(activity) { e ->
Log.d("appDebug", "FAILED : ${e.localizedMessage}")
}
If someone having same issue then just use this composable instead of rememberLauncherForActivityResult().
Thanks to #RĂ³bert Nagy
Ref: https://stackoverflow.com/a/65323208/15301088
I removed some deprecated codes from original post now it works fine for me.
#Composable
fun <I, O> registerForActivityResult(
contract: ActivityResultContract<I, O>,
onResult: (O) -> Unit
): ActivityResultLauncher<I> {
val owner = LocalContext.current as ActivityResultRegistryOwner
val activityResultRegistry = owner.activityResultRegistry
// Tracking current onResult listener
val currentOnResult = rememberUpdatedState(onResult)
// Only need to be unique and consistent across configuration changes.
val key = remember { UUID.randomUUID().toString() }
DisposableEffect(activityResultRegistry, key, contract) {
onDispose {
realLauncher.unregister()
}
}
return realLauncher
}
for e.g.
val registerActivityResult = registerForActivityResult(
contract = ActivityResultContracts.StartIntentSenderForResult()
) {
// handle your response
}
// just call launch and pass the contract
registerActivityResult.launch(/*Your Contract*/)

How to handle ResolvableApiException in Jetpack Compose?

I am trying to implement a CredentialRequest inside my composable, but I can't get the ResolvableApiException handling to work.
I have created an ActivityResultLauncher using rememberLauncherForActivityResult and I'm calling it from the OnCompleteListener to start the PendingIntent as you can see below.
I'd expect this to work, but for some reason I never receive the ActivityResult.
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartIntentSenderForResult()
) {
if (it.resultCode != RESULT_OK) {
return#rememberLauncherForActivityResult
}
// Handling ActivityResult here
}
val context = LocalContext.current
LaunchedEffect(Unit) {
val credentialsRequest = CredentialRequest.Builder()
.setAccountTypes("https://signin.example.com")
.build()
val credentialsClient = Credentials.getClient(context)
// Read the stored credential if user already signed in before
credentialsClient.request(credentialsRequest).addOnCompleteListener {
val result = try {
it.result?.credential?.id
} catch (exception: Exception) {
val resolvableException = exception.cause as? ResolvableApiException
if (resolvableException === null) {
// Exception not resolvable
return#addOnCompleteListener
}
// User must sign in first
launcher.launch(
IntentSenderRequest.Builder(resolvableException.resolution)
.build()
)
return#addOnCompleteListener
}
// Handling result here
}
}
I'm guessing that this might have to do with the launcher being outdated because of recomposition, but I'm not sure if that's even possible.
What do I have to do to receive the ActivityResult?

Handle no internet connection error of retrofit 2.6 with kotlin coroutines

I'm using retrofit 2.6 with kotlin coroutines to make API call without block the UI thread, I got it work but the app crashes when I switch off the internet connection. The logcat error is: E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
Here is my code:
private fun handleIntent(slug: String) {
val service = UtilityMethods.migrationTimeService()
UtilityMethods.showView(loading_view)
UtilityMethods.hideView(network_error_msg)
CoroutineScope(Dispatchers.IO).launch {
val res = service.getPostBySlug(slug)
try {
withContext(Dispatchers.Main) {
//Do something with response e.g show to the UI.
val post = res.body()!!.first()
UtilityMethods.hideView(loading_view)
val title = post.title?.rendered
val content = post.content?.rendered
val imageUrl = post.jetPackFeaturedMediaUrl
title_txtView.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
Html.fromHtml(title, Html.FROM_HTML_MODE_COMPACT).toString()
else
Html.fromHtml(title).toString()
content_txtView.loadData(content.toString(), "text/html", "UTF-8")
Picasso.get().load(imageUrl).fit().centerCrop().into(thumbnail_imgview)
}
} catch (e: HttpException) {
UtilityMethods.showView(network_error_msg)
} catch (e: Throwable) {
Toast.makeText(this#PostContentActivity, "Ooops: Something else went wrong", Toast.LENGTH_LONG)
}
}
}
I've got the code working, the new code is:
private fun handleIntent(slug: String) = GlobalScope.launch(Dispatchers.Main) {
val service = UtilityMethods.migrationTimeService()
UtilityMethods.showView(loading_view)
UtilityMethods.hideView(network_error_msg)
try {
val res = withContext(Dispatchers.IO) {
service.getPostBySlug(slug)
}
//Do something with response e.g show to the UI.
val post = res.body()!!.first()
UtilityMethods.hideView(loading_view)
val title = post.title?.rendered
val content = post.content?.rendered
val imageUrl = post.jetPackFeaturedMediaUrl
title_txtView.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
Html.fromHtml(title, Html.FROM_HTML_MODE_COMPACT).toString()
else
Html.fromHtml(title).toString()
content_txtView.loadData(content.toString(), "text/html", "UTF-8")
Picasso.get().load(imageUrl).fit().centerCrop().into(thumbnail_imgview)
}
catch (e: HttpException) {
Toast.makeText(this#PostContentActivity, "Exception ${e.message}", Toast.LENGTH_LONG).show()
}catch (e: IOException) {
UtilityMethods.hideView(loading_view)
UtilityMethods.showView(network_error_msg)
} catch (e: Throwable) {
Toast.makeText(this#PostContentActivity, "Ooops: Something else went wrong ${e.message}", Toast.LENGTH_LONG).show()
}
}
So while looking into stacktrace I found that ConnectException is thrown when network is unavailable
And that's how I do it in kotlin and it works for me,
suspend fun<T: Any> safeAPICall(call: suspend () -> Response<T>) : T{
val response = try {
call.invoke()
}
catch (e:java.lang.Exception){
e.printStackTrace()
val message = if( e is ConnectException) "Connection Error" else "Something went wrong. Please try again."
throw IOException(ResponseError(message, 500).convertToJsonString())
}
// When connection is OK
if(response.isSuccessful){
return response.body()!!
}else{
val error = response.errorBody()?.string()
error?.let{
val message = JSONObject(it).optString("message", "Something went wrong")
val responseError = ResponseError(message, response.code())
throw IOException(responseError.convertToJsonString())
}
throw IOException(ResponseError("Something went wrong. Please try again.", 500).convertToJsonString())
}
}
The data class that I use
data class ResponseError(val message:String, val errorCode:Int)
Usage:
try {
val response = safeAPICall {APIClient.planner.viewSites(view.context.authToken)}
}
catch (e:Exception){
view.snack(e.message?.toModel<ResponseError>()?.message?: unspecified_error)
}
Bonus:
inline fun <reified T> JSONObject.toModel(): T? = this.run {
try {
Gson().fromJson<T>(this.toString(), T::class.java)
}
catch (e:java.lang.Exception){ e.printStackTrace(); null }
}
inline fun <reified T> String.toModel(): T? = this.run {
try {
JSONObject(this).toModel<T>()
}
catch (e:java.lang.Exception){ null }
}
Instead of this:
CoroutineScope(Dispatchers.IO).launch {
val res = service.getPostBySlug(slug)
try {
withContext(Dispatchers.Main) {
Try this one:
CoroutineScope(Dispatchers.Main).launch {
val res = service.getPostBySlug(slug)
withContext(Dispatchers.IO) {
try {
wrap your 'try and catch' block code within Dispatchers.IO instead of wraping your Dispatchers.IO with in yout try block

Categories

Resources