I want to send email to user when some event occurs, I had searched on internet and I couldn't find how to do it.
Can anyone show me right path.
I used Javamail library for sending email to user with the help of sendgrid.net email server.
Then I just implemented at what event I wanted to send email to the users.
Tips: Use latest version of Javamail and don't forget to turn on Internet Permission in Manifest file
private fun sendMail(etEmail: EditText, etSubject: EditText, etMessage: EditText) {
// Set up the mail server
val host = "smtp.sendgrid.net"
val props = Properties().apply {
put("mail.smtp.auth", "true")
put("mail.smtp.ssl.enable", "true")
put("mail.smtp.host", host)
put("mail.smtp.port", "465")
}
// Set up authentication
val session = Session.getInstance(props, object : Authenticator() {
override fun getPasswordAuthentication() =
PasswordAuthentication("apikey","yourpaswordxyzfromsendgridaccount")
})
try {
// Create a default MimeMessage object
val message = MimeMessage(session).apply {
setFrom(InternetAddress("abc#xyz"))
addRecipient(Message.RecipientType.TO, InternetAddress(etEmail.text.toString()))
subject = etSubject.text.toString()
setText(etMessage.text.toString())
}
// Send the message
thread(start = true) {
Transport.send(message)
println("Email sent successfully.")
println("running from thread(): ${Thread.currentThread()}")
}
Toast.makeText(this,"Mail sent",Toast.LENGTH_LONG).show()
} catch (e: MessagingException) {
e.printStackTrace()
}
}
Related
I have successfully integrated Linphone SDK in my project with their dependency.
implementation 'org.linphone:linphone-sdk-android:5.1.59'
// Adding this dependency allows the linphone-sdk to automatically handle audio focus
implementation 'androidx.media:media:1.6.0'
And It is working completely ok when using credentials of linphone.But When I am trying to use our sip credentials of PBX It throws io error
I have tested our credentials of our local network in Linphone Android App It works fine. But when try to login in my app It throws error.
I have added this code for login in SIP.
fun login(domain: String, username: String, password: String) {
val mgr: ConnectivityManager =
getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val listAddress: MutableList<String> = ArrayList()
mgr.getLinkProperties(mgr.activeNetwork)?.let{network->
network.dnsServers.forEach {
it.hostAddress?.let { it1 -> listAddress.add(it1) }
}
}
core.setDnsServers(listAddress.map { it }.toTypedArray())
val authInfo =
Factory.instance().createAuthInfo(username, null, password, null, null, domain, null)
val params = core.createAccountParams()
val senderUri = "sip:$username#$domain"
val identity = Factory.instance().createAddress(senderUri)
params.identityAddress = identity
val address = Factory.instance().createAddress("sip:$domain")
address?.transport = TransportType.Tls
params.serverAddress = address
params.isOutboundProxyEnabled = true
params.isRegisterEnabled = true
val account = core.createAccount(params)
getInstance().core.addAuthInfo(authInfo)
getInstance().core.addAccount(account)
getInstance().core.defaultAccount = account
core.start()
account.addListener { _, state, message ->
Log.e(TAG, "login: state $state $message" )
if ("$state" == "Failed") {
Utils().showShortToast(getInstance(), "Registration Failed")
} else if ("$state" == "Ok") {
Utils().showShortToast(getInstance(), "Registration Success")
}
}
}
I think your issue is that you try to manually set the DNS servers.
Try removing this part of your code:
val mgr: ConnectivityManager =
getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val listAddress: MutableList<String> = ArrayList()
mgr.getLinkProperties(mgr.activeNetwork)?.let{network->
network.dnsServers.forEach {
it.hostAddress?.let { it1 -> listAddress.add(it1) }
}
}
core.setDnsServers(listAddress.map { it }.toTypedArray())
Linphone-SDK already handles that part.
Otherwise it looks OK. If issue persists enable debug logs
Factory.instance().setLogCollectionPath(context.filesDir.absolutePath)
Factory.instance().enableLogCollection(LogCollectionState.Enabled)
Factory.instance().setLoggerDomain(appName)
Factory.instance().enableLogcatLogs(true)
Factory.instance().loggingService.setLogLevel(LogLevel.Message)
and attach them.
I am using android javamail library 1.6.2. I am trying to read mails and return it as a list of custom objects in fragment to display them in recycler view. The code I am using for reading mails is:
fun readMails(host: String, port: String,
username: String, password: String): List<Mail>? {
var folder: Folder? = null
var store: Store? = null
return try {
val properties = Properties()
properties[HOST] = host
properties[PORT] = port
properties[START_TLS] = "true"
val session = Session.getDefaultInstance(properties)
// Create IMAP store object and connect with the server
store = session.getStore(PROTOCOL)
store.connect(host, username, password)
// Create folder object and open it in read-only mode
folder = store.getFolder(FOLDER_TYPE)
folder.open(Folder.READ_ONLY)
// Fetch messages from the folder and print in a loop
val messages = folder.messages
val mails = messages.map {
Mail(
messageNumber = it.messageNumber,
subject = it.subject,
senders = it.from.toList().map { address ->
MailAddress(
type = address.type,
)
},
content = parseContent(it.content as Multipart)
)
}
Log.d(TAG, "readMails: $mails")
mails
} catch (e: NoSuchProviderException) {
Log.e(TAG, "NoSuchProviderException: ${e.localizedMessage}")
null
} catch (e: MessagingException) {
Log.e(TAG, "MessagingException: ${e.localizedMessage}")
null
} catch (e: Exception) {
Log.e(TAG, "Exception: ${e.localizedMessage}")
null
} finally {
folder?.close(false)
store?.close()
}
}
In fragment I am trying to read mails using:
viewLifecycleOwner.lifecycleScope.launch {
val emails = MailHelper.readMails(
host = "",
port = "",
username = "",
password = ""
)
mailAdapter.submitList(emails)
}
The problem is that I can print mails in console but I can only print them using GlobalScope.launch {}. If I use that I cannot display then in recyclerview using submitList() to the adapter. If I use viewLifecycleOwner.lifecycleScope.launch {} I keep getting android.os.NetworkOnMainThreadException.
Your problem arises from the fact that viewLifecycleOwner.lifecycleScope is bound to Dispatchers.Main.immediate which is confined to the application 'Main' or 'UI' thread meaning your coroutine starts executing on UI thread and you get the error.
To solve this you should pass IO dispatcher to the launch function
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
val emails = MailHelper.readMails(
host = "",
port = "",
username = "",
password = ""
)
mailAdapter.submitList(emails)
}
This will make sure that your coroutine executes on thread pool allocated for IO and not on the main thread.
Note : Dispatchers.IO can't be used to update UI, only UI thread can do that
I'm trying to set up a Google OneTap SignIn button to my developed App (I'm not using Firebase to sign in) guiding by this source:
https://developers.google.com/identity/one-tap/android/get-started
I've created both OAuth & Web credentials on Cloud Console. To generate OAuth Id I took SHA1 which was provided by Android Studio in signing-in report (I took develop SHA-1, but they are all the same anyway).
I've put to R.string.default_web_client_id the Client Id from WebAuth (Not an Android Id from OAuth).
As I use Firebase RTDB for storing some data, I set this SHA-1 there as well. The project name in Manifest, Cloud Console and Firebase Console are the same. From Firebase I downloaded "google-services.json" and put it in app root. On Firebase I also set valid service email.
This is how I implemented OneTap on login activity:
class LoginActivity : AppCompatActivity() {
...
private lateinit var oneTapClient: SignInClient
private lateinit var signUpRequest: BeginSignInRequest
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
oneTapClient = Identity.getSignInClient(this)
signUpRequest = BeginSignInRequest.builder()
.setPasswordRequestOptions(BeginSignInRequest.PasswordRequestOptions.builder()
.setSupported(false)
.build())
.setGoogleIdTokenRequestOptions(BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
.setSupported(true)
// Your server's client ID, not your Android client ID.
.setServerClientId(getString(R.string.default_web_client_id))
// Only show accounts previously used to sign in.
.setFilterByAuthorizedAccounts(false)
.build())
// Automatically sign in when exactly one credential is retrieved.
.setAutoSelectEnabled(false)
.build()
}
// THIS CALLED FROM FRAGMENT WHEN GOOGLE BUTTON IS CLICKED
fun onGoogleClick() {
when (GoogleApiAvailability().isGooglePlayServicesAvailable(applicationContext)) {
0 -> {
beginGoogleSignIn()
}
1 -> {
toaster.show("Google services required")
}
2 -> {
toaster.show("Google services update required")
}
}
}
private fun beginGoogleSignIn() {
val tag = "$atag-beginGoogleSignIn"
oneTapClient.beginSignIn(signUpRequest)
.addOnSuccessListener(this) { result ->
try {
startIntentSenderForResult(
result.pendingIntent.intentSender, library.GOOGLE_SIGNIN_REQUEST_CODE,
null, 0, 0, 0)
} catch (e: IntentSender.SendIntentException) {
logg.d(atag, "Couldn't start One Tap UI: ${e.localizedMessage}")
}
}
.addOnFailureListener(this) { e ->
// No Google Accounts found. Just continue presenting the signed-out UI.
toaster.show("Please sign in to your google account on your phone")
logg.d(tag, e.localizedMessage)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val tag = "$atag-onActivityResult"
super.onActivityResult(requestCode, resultCode, data)
// Result returned from launching the Intent from GoogleSignInClient.getSignInIntent(...);
if (requestCode == library.GOOGLE_SIGNIN_REQUEST_CODE) {
try {
val credential = oneTapClient.getSignInCredentialFromIntent(data)
token = credential.googleIdToken
when {
token != null -> {
// Got an ID token from Google. Use it to authenticate
// with your backend.
login = credential.id
name = credential.displayName
avatarUrl = credential.profilePictureUri.toString()
tryToLogin()
}
else -> {
// Shouldn't happen.
logg.d(atag, "No ID token!")
}
}
} catch (e: ApiException) {
when (e.statusCode) {
CommonStatusCodes.CANCELED -> {
toaster.show("Please sign in to your google account on your phone")
logg.d(tag, "One-tap dialog was closed.")
// Don't re-prompt the user.
}
CommonStatusCodes.NETWORK_ERROR -> {
logg.d(tag, "One-tap encountered a network error.")
// Try again or just ignore.
}
else -> {
logg.d(tag, "Couldn't get credential from result." +
" (${e.localizedMessage})")
}
}
}
}
}
}
The main problem is that on some of AVD it works well, on other AVD id gives an Error:
16: Cannot find a matching credential.
However on this AVD Google Services is up to date, and user is logged in to Google Play
On real device I got this error:
10: Caller not whitelisted to call this API.
Google services is also up to date here and user is logged in to Play Store.
Everywhere I used my real gmail address.
What can be wrong?
UPDATE
Spending a lot of time trying to solve this I have just figured out that OneTap is working on some devices, and is not working on other. Also tried to re-create credentials several times.
Not having very much time to solve this I just use both OneTap way and alternate way to sign in with Google credentials.
// In activity
lateinit var oneTapClient: SignInClient
lateinit var signUpRequest: BeginSignInRequest
private lateinit var gso: GoogleSignInOptions
lateinit var mGoogleSignInClient: GoogleSignInClient
private fun initApp() {
oneTapClient = Identity.getSignInClient(this)
signUpRequest = BeginSignInRequest.builder()
.setPasswordRequestOptions(
BeginSignInRequest.PasswordRequestOptions.builder()
.setSupported(false)
.build()
)
.setGoogleIdTokenRequestOptions(
BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
.setSupported(true)
// Your server's client ID, not your Android client ID.
.setServerClientId(getString(R.string.default_web_client_id))
// Only show accounts previously used to sign in.
.setFilterByAuthorizedAccounts(false)
.build()
)
// Automatically sign in when exactly one credential is retrieved.
.setAutoSelectEnabled(false)
.build()
gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestProfile()
.requestEmail()
.requestIdToken(getString(R.string.default_web_client_id))
.build()
mGoogleSignInClient = GoogleSignIn.getClient(this, gso)
}
// Somewhere else in fragment, 'parent' is reference to activity
// Call this when button clicked
private fun beginGoogleSignIn() {
parent.oneTapClient.beginSignIn(parent.signUpRequest)
.addOnSuccessListener(parent) { result ->
try {
startIntentSenderForResult(
result.pendingIntent.intentSender, state.library.GOOGLE_SIGNIN_REQUEST_CODE,
null, 0, 0, 0, null)
} catch (e: IntentSender.SendIntentException) {
stopSignIn("Couldn't start One Tap UI: ${e.localizedMessage}")
}
}
.addOnCanceledListener(parent) {
stopSignIn("Cancelled")
}
.addOnFailureListener(parent) { e ->
// Use alternate sign in
val signInIntent = parent.mGoogleSignInClient.signInIntent
startActivityForResult(signInIntent, state.library.GOOGLE_ALT_SIGNIN_REQUEST_CODE)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Result returned from launching the Intent from GoogleSignInClient.getSignInIntent(...);
if (requestCode == state.library.GOOGLE_SIGNIN_REQUEST_CODE) {
try {
handleOneTapSignIn(data)
} catch (e: ApiException) {
val message: String
when (e.statusCode) {
CommonStatusCodes.CANCELED -> {
message = "Please sign in to your google account on your phone"
// Don't re-prompt the user.
}
CommonStatusCodes.NETWORK_ERROR -> {
// Try again or just ignore.
message = "One-tap encountered a network error."
}
else -> {
message = "Couldn't get credential from result." +
" (${e.localizedMessage})"
}
}
stopSignIn(message)
}
} else if (requestCode == state.library.GOOGLE_ALT_SIGNIN_REQUEST_CODE) {
try {
handleAlternateSignIn(data)
} catch (e: ApiException) {
if (e.statusCode == 12501) {
// Dismissed
stopSignIn()
} else {
stopSignIn("${e.statusCode}")
}
}
}
}
private fun handleOneTapSignIn(data: Intent?) {
val credential = parent.oneTapClient.getSignInCredentialFromIntent(data)
val token = credential.googleIdToken
when {
token != null -> {
// Got an ID token from Google. Use it to authenticate
// with your backend.
login = credential.id
avatarUrl = credential.profilePictureUri?.toString() ?: ""
tryToLogin()
}
else -> {
// Shouldn't happen.
stopSignIn("No ID token!")
}
}
}
private fun handleAlternateSignIn(data: Intent?) {
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
try {
val account = task.getResult(ApiException::class.java)
// Signed in successfully, show authenticated UI.
login = account.email!!
token = account.idToken ?: ""
avatarUrl = account.photoUrl?.toString() ?: ""
tryToLogin()
} catch (e: ApiException) {
// The ApiException status code indicates the detailed failure reason.
// Please refer to the GoogleSignInStatusCodes class reference for more information.
val message = when (e.statusCode) {
CommonStatusCodes.NETWORK_ERROR -> {
"Could not reach network"
}
else -> {
"SignIn failed with exception $e"
}
}
stopSignIn(message)
}
}
UPDATE 2
It seems 16: Cannot find a matching credential on Android Emulators is related not to user credentials attempting to sign in, but an app credential in Google Cloud Console.
The issue is not reproduced in my case when I filled consent page at Google Cloud Console as described at Get started with One Tap sign-in and sign-up
(I have left empty fields for links to EULA, etc.). Then I have changed package name (for another reasons), then double check that this package name was the same in both credentials configs on Google Cloud Console and in google-services.json. Also I checked that google-services.json is located under [project_root]/app directory.
I will check this on real device in a few days.
I am developing an Android app where all my users who logged inside my application should be remembered. More specifically, get their Device Key register in their user profile. The problem here is my currSignedDevice shows “null” as shown below and when I try to remember it, It is not being remembered.
I am using AWS Cognito and followed their documentation here https://aws.amazon.com/blogs/mobile/tracking-and-remembering-devices-using-amazon-cognito-your-user-pools/. I set my device settings in Cognito and implemented with code below.
fun getSignInUserDeviceDetails(user: String?):Boolean {
var currSignedDevice = userPool.getUser (user).thisDevice();
println("device is "+ currSignedDevice +"\t")
var changeDeviceSettingsHandler: GenericHandler = object : GenericHandler {
override fun onSuccess() {
// Device status successfully changed
println("device remembered successfully")
}
override fun onFailure(exception: java.lang.Exception) {
// Probe exception for the cause of the failure
println("failure in remember device")
}
}
currSignedDevice?.rememberThisDevice(changeDeviceSettingsHandler)
return true
}
Called this function in my loginfragment after the valid authentication.
login.setOnClickListener {
val email = email.text.toString()
val password = password.text.toString()
if (email.trim().isEmpty()) {
toastError("Enter an email.")
return#setOnClickListener
}
if (password.trim().isEmpty()) {
toastError("Enter a password.")
return#setOnClickListener
}
emailVal = email.toLowerCase()
passwordVal = password
signInDialog?.show()
activity?.let {
(it as MainActivity).setDialog50PercentWidth(signInDialog)
}
viewModel.authenticate(email, password, confirmUserHandler)
AWSClient.instance.getSignInUserDeviceDetails(emailVal)
}
The problem here is my currSignedDevice shows “null” as shown below in the figure and when I try to remember it, It is not being remembered. This is also not going inside of success or failure. It’s directly jumping out from that block.
fun getSignInUserDeviceDetails(user: String?):Boolean {
var currSignedDevice = userPool.getUser (user).thisDevice();
println("device is "+ currSignedDevice +"\t") // gives my currSignedDevice as NULL when I debug
var changeDeviceSettingsHandler: GenericHandler = object : GenericHandler {
override fun onSuccess() {
println("device remembered successfully")
}
override fun onFailure(exception: java.lang.Exception) {
// Probe exception for the cause of the failure
println("failure in remeber device")
}
}
currSignedDevice.rememberThisDevice(changeDeviceSettingsHandler)
return true
}
I'm trying to use AWS Cognito user pools in combination with the AWS API Gateway.
Logging in works fine, when entering the credentials the success handler is called and I can see the credentials. When verifying the JWT token at jwt.io I can also see that the user is correct.
However, when calling the API Gateway using the ApiClientFactory I always receive an error: com.amazonaws.mobileconnectors.apigateway.ApiClientException: Basic (classic) flow is not supported with RoleMappings, please use enhanced flow. (Service: AmazonCognitoIdentity; Status Code: 400; Error Code: InvalidParameterException; Request ID: 1a61f1fd-91d8-11e8-82bc-675071b1c307) (Service: null; Status Code: 0; Error Code: null; Request ID: null)
Please see my code below:
Main activity:
AWSMobileClient.getInstance().initialize(this) {
// Obtain the reference to the AWSCredentialsProvider and AWSConfiguration objects
// Use IdentityManager#getUserID to fetch the identity id.
IdentityManager.getDefaultIdentityManager().getUserID(object : IdentityHandler {
override fun onIdentityId(identityId: String) {
Log.d("MainActivity", "Identity ID = " + identityId)
}
override fun handleError(exception: Exception) {
Log.d("MainActivity", "Error in retrieving the identity" + exception)
}
})
}.execute()
LoginFragment:
val authenticationHandler = object : AuthenticationHandler {
override fun getAuthenticationDetails(continuation: AuthenticationContinuation, userID: String) {
val authDetails = AuthenticationDetails(inputUsername.text.toString(), inputPassword.text.toString(), null)
// Now allow the authentication to continue
continuation.setAuthenticationDetails(authDetails)
continuation.continueTask()
}
override fun onSuccess(userSession: CognitoUserSession, newDevice: CognitoDevice?) {
progressLoader.visibility = View.GONE
(activity as? OnboardingActivity)?.proceedAfterLogin()
}
override fun onFailure(exception: Exception) {
progressLoader.visibility = View.GONE
val snackbar = Snackbar.make(view, R.string.ERR_GENERAL, Snackbar.LENGTH_LONG)
snackbar.show()
progressLoader.visibility = View.GONE
}
override fun getMFACode(continuation: MultiFactorAuthenticationContinuation) {
continuation.continueTask()
}
override fun authenticationChallenge(continuation: ChallengeContinuation) {
continuation.continueTask()
}
}
loginButton.setOnClickListener {
val userPool = CognitoUserPool(context, AWSMobileClient.getInstance().configuration)
val user = userPool.getUser(inputUsername.text.toString())
progressLoader.visibility = View.VISIBLE
user.getSessionInBackground(authenticationHandler)
}
Api client:
val factory = ApiClientFactory().credentialsProvider(AWSMobileClient.getInstance().credentialsProvider)
val = factory.build(MyClient::class.java)
try {
val request = GetChallengesRequest("", nextPageKey)
val response = client.getRunningChallenges(request)
} catch (t: Throwable) {
// This catch is allways called with the error
}
The config is loaded using the awsconfiguration.json which is stored in the raw resource folder.
When setting a breakpoint in AWS4Signer sign method I can see the sign method is called with AnonymousAWSCredentials but I really can't figure out why, as I call the method after logging in.
I hope someone can help me resolve this weird issue, it's been bugging me for days!