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.
Related
I'm trying to integrate One Tap Sign in with Google into my app which I'm building with Jetpack Compose. I'm using startIntentSenderForResult to launch an intent, but now the problem is that I'm unable to receive activity result from my composable function. I'm using rememberLauncherForActivityResult to get the result from an intent but still not getting anywhere. Any solutions?
LoginScreen
#Composable
fun LoginScreen() {
val activity = LocalContext.current as Activity
val activityResult = remember { mutableStateOf<ActivityResult?>(null) }
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
val oneTapClient = Identity.getSignInClient(activity)
val credential = oneTapClient.getSignInCredentialFromIntent(result.data)
val idToken = credential.googleIdToken
if (idToken != null) {
// Got an ID token from Google. Use it to authenticate
// with your backend.
Log.d("LOG", idToken)
} else {
Log.d("LOG", "Null Token")
}
Log.d("LOG", "ActivityResult")
if (result.resultCode == Activity.RESULT_OK) {
activityResult.value = result
}
}
activityResult.value?.let { _ ->
Log.d("LOG", "ActivityResultValue")
}
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
GoogleButton(
onClick = {
signIn(
activity = activity
)
}
)
}
}
fun signIn(
activity: Activity
) {
val oneTapClient = Identity.getSignInClient(activity)
val signInRequest = BeginSignInRequest.builder()
.setGoogleIdTokenRequestOptions(
BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
.setSupported(true)
// Your server's client ID, not your Android client ID.
.setServerClientId(CLIENT_ID)
// Only show accounts previously used to sign in.
.setFilterByAuthorizedAccounts(true)
.build()
)
// Automatically sign in when exactly one credential is retrieved.
.setAutoSelectEnabled(true)
.build()
oneTapClient.beginSignIn(signInRequest)
.addOnSuccessListener(activity) { result ->
try {
startIntentSenderForResult(
activity, result.pendingIntent.intentSender, ONE_TAP_REQ_CODE,
null, 0, 0, 0, null
)
} catch (e: IntentSender.SendIntentException) {
Log.e("LOG", "Couldn't start One Tap UI: ${e.localizedMessage}")
}
}
.addOnFailureListener(activity) { e ->
// No saved credentials found. Launch the One Tap sign-up flow, or
// do nothing and continue presenting the signed-out UI.
Log.d("LOG", e.message.toString())
}
}
You aren't actually calling launch on the launcher you create, so you would never get a result back there.
Instead of using the StartActivityForResult contract, you need to use the StartIntentSenderForResult contract - that's the one that takes an IntentSender like the one you get back from your beginSignIn method.
This means your code should look like:
#Composable
fun LoginScreen() {
val context = LocalContext.current
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.StartIntentSenderForResult()
) { result ->
if (result.resultCode != Activity.RESULT_OK) {
// The user cancelled the login, was it due to an Exception?
if (result.data?.action == StartIntentSenderForResult.ACTION_INTENT_SENDER_REQUEST) {
val exception: Exception? = result.data?.getSerializableExtra(StartIntentSenderForResult.EXTRA_SEND_INTENT_EXCEPTION)
Log.e("LOG", "Couldn't start One Tap UI: ${e?.localizedMessage}")
}
return#rememberLauncherForActivityResult
}
val oneTapClient = Identity.getSignInClient(context)
val credential = oneTapClient.getSignInCredentialFromIntent(result.data)
val idToken = credential.googleIdToken
if (idToken != null) {
// Got an ID token from Google. Use it to authenticate
// with your backend.
Log.d("LOG", idToken)
} else {
Log.d("LOG", "Null Token")
}
}
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
// Create a scope that is automatically cancelled
// if the user closes your app while async work is
// happening
val scope = rememberCoroutineScope()
GoogleButton(
onClick = {
scope.launch {
signIn(
context = context,
launcher = launcher
)
}
}
)
}
}
suspend fun signIn(
context: Context,
launcher: ActivityResultLauncher<IntentSenderRequest>
) {
val oneTapClient = Identity.getSignInClient(context)
val signInRequest = BeginSignInRequest.builder()
.setGoogleIdTokenRequestOptions(
BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
.setSupported(true)
// Your server's client ID, not your Android client ID.
.setServerClientId(CLIENT_ID)
// Only show accounts previously used to sign in.
.setFilterByAuthorizedAccounts(true)
.build()
)
// Automatically sign in when exactly one credential is retrieved.
.setAutoSelectEnabled(true)
.build()
try {
// Use await() from https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-play-services
// Instead of listeners that aren't cleaned up automatically
val result = oneTapClient.beginSignIn(signInRequest).await()
// Now construct the IntentSenderRequest the launcher requires
val intentSenderRequest = IntentSenderRequest.Builder(result.pendingIntent).build()
launcher.launch(intentSenderRequest)
} catch (e: Exception) {
// No saved credentials found. Launch the One Tap sign-up flow, or
// do nothing and continue presenting the signed-out UI.
Log.d("LOG", e.message.toString())
}
}
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")
)
I am a beginner in android application development(Kotlin) and recently I was handover a project on NFT which involves walletConnect integration & for that I am using the walletConnectV1 library.
Fetching the public key and Connecting with metamask was not so hard but I am struggling when it comes to signing methods.
if anyone can help me with, how to sign messages and transactions or what I was doing wrong all this time that would really help me.
Thank you
Connect Button Click Listener
screen_main_connect_button.setOnClickListener {
try {
ExampleApplication.resetSession()
ExampleApplication.session.addCallback(this)
val i = Intent(Intent.ACTION_VIEW, Uri.parse(ExampleApplication.config.toWCUri()))
startActivity(i)
} catch (e: ActivityNotFoundException) {
// open play store
} catch (e: Exception) {
//handle exceptions
}
}
Response after the session was approved
private fun sessionApproved() {
uiScope.launch {
val account = session.approvedAccounts()?.get(0)?:""
screen_main_status.text = "Connected: $account"
screen_main_connect_button.visibility = View.GONE
screen_main_disconnect_button.visibility = View.VISIBLE
screen_main_tx_button.visibility = View.VISIBLE
val job = async {
personalSign(
"Sign this message of mine to this address",
account) {
Log.d(TAG, "sessionApproved: ${it.result}")
}
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("wc:")
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
}
}
private fun personalSign(
message: String,
address: String,
response: (Session.MethodCall.Response) -> Unit
) {
val id = System.currentTimeMillis()
val messageParam = if (message.hasHexPrefix()) message else message.toHex()
session.performMethodCall(
Session.MethodCall.Custom(
id, "personal_sign", listOf(messageParam, address)
)
) { response(it) }
}
I am working with camera permissions. when the user clicks on take photo button, user will be shown with run time permissions menu and lets say user deny's it and then clicks on take photo button, run time permissions will be shown second time.
after this clicking the take photo button nothing happens.
What I want to do is, after the second attempt, i want to show a popup telling the user to go to settings to change the permissions.
How can I know if the user has denied the permission twice.
this is what I have coded so far
takePhotoBtn.setOnClickListener {
takePhoto()
}
private fun takePhoto() {
activity?.let {
if (isCameraPermissionsAllowed()) {
capturePhoto()
} else {
permReqLauncher.launch(
CAMERA_PERMISSION
)
}
}
}
private val permReqLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
val granted = permissions.entries.all {
it.value == true
}
if (granted) {
capturePhoto()
}
}
private fun capturePhoto() {
onUtilityBillTypeListener.onUtilityBillTypePhotoLink(true)
}
where is the right place to add this dinielDialog
private fun showPermissionDeniedDialog() {
AlertDialog.Builder(this.requireContext())
.setTitle("Permission Denied")
.setMessage("Permission is denied, Please allow permissions from App Settings.")
.setPositiveButton("Settings",
DialogInterface.OnClickListener { dialogInterface, i ->
// send to app settings if permission is denied permanently
val intent = Intent()
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
val uri = Uri.fromParts("package", getPackageName(this.requireContext()), null)
intent.data = uri
startActivity(intent)
})
.setNegativeButton("Cancel", null)
.show()
}
You could write something like this
if (isCameraPermissionsAllowed()) {
capturePhoto()
} else {
if (permissionDeniedFlag) {
showPermissionDeniedDialog()
} else {
permissionDeniedFlag = True
permReqLauncher.launch(
CAMERA_PERMISSION
)
}
With an initial declaration of var permissionDeniedFlag = False.
On your code where you're getting permission result you can do it like this :
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
isGranted: Boolean ->
if (isGranted) //perform action
else {
val builder = AlertDialog.Builder(requireContext())
builder.setTitle("Permission Required!")
builder.setMessage("We need permission in order to perform this action.")
builder.setPositiveButton("OK") { dialog, _ ->
showPermRationale()
dialog.cancel()
}
builder.setNegativeButton("CANCEL") { dialog, _ ->
dialog.cancel()
}
builder.show()
}
}
private fun showPermRationale() {
val intent = Intent()
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
val uri = Uri.fromParts(
"package", requireActivity().packageName, null)
intent.data = uri
requireActivity().startActivity(intent)
}
resolved it by adding this code
private fun takePhoto() {
activity?.let {
if (isCameraPermissionsAllowed()) {
capturePhoto()
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(
this.requireActivity(),
Manifest.permission.CAMERA
)
) {
showPermissionDeniedDialog()
}
else{
permReqLauncher.launch(
CAMERA_PERMISSION
)
}
}
}
}
I want to redirect the user to another activity that opens a internet url. On button click.
Here is my code so far
Button(onClick = {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"))
// Here is some code that starts the activity
}) {
Text(text="APPLY HACK")
}
You can use something like:
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"))
val context = LocalContext.current
Button(onClick = {
startActivity(context, intent, null) }
) {
Text("BUTTON")
}
val context = LocalContext.current
Button(onClick = {
context.startActivity(Intent(Intent.ACTION_VIEW,Uri.parse("http://www.google.com"))) }
) {
Text("Click here")
}