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
Related
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()
}
}
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 TheMovieDB API to call the JSON Response of the popular movies and put it into a ScrollView. I have ensured that I have done all the neccessary steps to get the API. However it does not work and does not display anything. If I use another API: "https://jsonplaceholder.typicode.com/posts", it works and the JSON data is displayed into the ScrollView.
Network Utils:
companion object {
private val TAG: String = NetworkUtils::class.java!!.simpleName
private val JSON_RESPONSE_URL = "https://api.themoviedb.org/3/movie/popular?api_key=myapikeyishere"
private val TYPE_SINGLE = 1
private val TYPE_ALL = 0
/**
* Builds the URL used to talk to the weather server using a location. This location is based
* on the query capabilities of the weather provider that we are using.
*
* #param locationQuery The location that will be queried for.
* #return The URL to use to query the weather server.
*/
fun buildURLSingleType(id: Int): URL {
return buildUrl(
TYPE_SINGLE,
id
)
}
fun buildURLAll(): URL {
return buildUrl(
TYPE_ALL,
0
)
}
private fun buildUrl(type: Int, id: Int): URL {
var uri = Uri.parse(JSON_RESPONSE_URL).buildUpon()
if (type == TYPE_SINGLE) {
uri.appendPath("1").build()
}
val builtUri = uri.build()
var url: URL? = null
try {
url = URL(builtUri.toString())
} catch (e: MalformedURLException) {
e.printStackTrace()
}
Log.v(TAG, "Built URI " + url!!)
return url
}
/**
* This method returns the entire result from the HTTP response.
*
* #param url The URL to fetch the HTTP response from.
* #return The contents of the HTTP response.
* #throws IOException Related to network and stream reading
*/
#Throws(IOException::class)
fun getResponseFromHttpUrl(url: URL): String? {
val urlConnection = url.openConnection() as HttpURLConnection
try {
val `in` = urlConnection.getInputStream()
val scanner = Scanner(`in`)
scanner.useDelimiter("\\A")
val hasInput = scanner.hasNext()
return if (hasInput) {
scanner.next()
} else {
null
}
} catch (ex: Exception) {
Log.d(TAG, ex.toString())
} finally {
urlConnection.disconnect()
}
return null
}
}
The Activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_network)
}
override fun onResume() {
super.onResume()
val scope = CoroutineScope(Job() + Dispatchers.IO)
var nwMutipleItemsJob = scope.async(Dispatchers.IO)
{
var nwURL = NetworkUtils.buildURLAll()
val response = NetworkUtils.getResponseFromHttpUrl(nwURL)
response
}
scope.async(Dispatchers.Default)
{
var response = nwMutipleItemsJob.await()
var jsonResponse = JSONArray(response)
var msg = "$response\n\n"
for(i in 0 until jsonResponse.length())
{
var jsonItem = jsonResponse.getJSONObject(i)
// var userid = jsonItem.getInt("userId")
// var id = jsonItem.getInt("adult")
// var title = jsonItem.getString("title")
// var body = jsonItem.getString("body")
// msg += "item $i\n\nid = \n = $id\n"
}
withContext(Dispatchers.Main)
{
tvJSONMultipleItemDisplay.text = msg
}
}
}
}
layout activity:
<ScrollView
android:layout_width="wrap_content"
android:layout_height="match_parent">
<TextView
android:id="#+id/tvJSONMultipleItemDisplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
/>
</ScrollView>
However it does not work and does not display anything. ? Did you find any error ?.
Please add logs in your getResponseFromHttpUrl and onResume that will help you to find and fix the issue.
How to debug ?.
Ref : https://stackoverflow.com/a/62019186/9909365
On the Android, the log of device display on Tab Logcat, not on console as the Chrome. You can see the log in this tab, and remember build your App in the debug mode.
Edit: If you want to see all the log, you can switch the option Show only Application selected to No filter
For more information, you can find in this link
How to add logs ?
Ref : https://developer.android.com/studio/debug/am-logcat
The Logcat window in Android Studio displays system messages, such as when a garbage collection occurs, and messages that you added to your app with the Log class. It displays messages in real time and keeps a history so you can view older messages.
To display just the information of interest, you can create filters, modify how much information is displayed in messages, set priority levels, display messages produced by app code only, and search the log. By default, logcat shows the log output related to the most recently run app only.
When an app throws an exception, logcat shows a message followed by the associated stack trace containing links to the line of code.
As of Android Studio 2.2, the Run window also displays log messages for the current running app. Note that you can configure the logcat output display, but not the Run window.
Write log messages
Ref : https://developer.android.com/studio/debug/am-logcat#WriteLogs
The Log class allows you to create log messages that appear in logcat. Generally, you should use the following log methods, listed in order from the highest to lowest priority (or, least to most verbose):
Log.e(String, String) (error)
Log.w(String, String) (warning)
Log.i(String, String) (information)
Log.d(String, String) (debug)
Log.v(String, String) (verbose)
I want to consume twitter streaming api in android.
I've used kotlin coroutines and retrofit.
Somehow in the third request i get an HTTP 420 ERROR (Enhance your calm)
I cannot understand why this happens. I am using kotlin coroutines.
Here's my code:
fun getStreamData(str: String) {
Log.d("debug", "Fetching data..")
coroutineScope.launch {
withContext(Dispatchers.Main) {
//Display loading animation in UI
_status.value = DataApiStatus.LOADING
}
try {
val listResult = ApiService().api!!.getTweetList(str).await()
while (!listResult.source().exhausted()) {
val reader = JsonReader(InputStreamReader(listResult.byteStream()))
// https://stackoverflow.com/questions/11484353/gson-throws-malformedjsonexception
reader.setLenient(true);
val gson = GsonBuilder().create()
val j = gson.fromJson<JsonObject>(reader, JsonObject::class.java)
Log.d("debug", "JSON: " + j.toString())
if (j.get("text") != null && j.getAsJsonObject("user").get("profile_image_url_https") != null && j.getAsJsonObject("user").get("name") != null){
val t = gson.fromJson<Tweet>(j, Tweet::class.java)
withContext(Dispatchers.Main) {
_status.value = DataApiStatus.DONE
// https://stackoverflow.com/questions/47941537/notify-observer-when-item-is-added-to-list-of-livedata
tweetsList.add(t)
_tweetsList.value = tweetsList
}
}
}
}
catch (e : JsonSyntaxException) {
Log.e("error", "JsonSyntaxException ${e.message}");
}
catch (e: Exception) {
Log.e("error", "ERROR ${e.message}")
}
}
}
This function is responsible to search the stream accordingly to str string which is a parameter.
Also, when the search parameter changes i cancel the current job and relaunch a new one with the actual search parameter.
fun cancelJob(){
Log.d("debug", "Cancelling current Job!")
coroutineScope.coroutineContext.cancelChildren()
}
What am i doing wrong? In the third request i get an HTTP 420 ERROR.
Here's the full code:
https://github.com/maiamiguel/RHO-Challenge
The 420 Enhance Your Calm status code is an unofficial extension by Twitter. Twitter used this to tell HTTP clients that they were being rate limited. Rate limiting means putting restrictions on the total number of requests a client may do within a time period.
I've been trying to use khttp to send an .jpg file in an android activity but haven't been able to make it work.
fun sendImage(view: View) {
try {
var bmp = (imageView?.drawable as BitmapDrawable).bitmap
var bos = ByteArrayOutputStream()
bmp.compress(Bitmap.CompressFormat.JPEG, 0, bos)
var response: Response? = null
findViewById<TextView>(R.id.image_desc).text = "Connecting to " + SERVER_URL;
try {
val job=GlobalScope.launch {
response = post(SERVER_URL, files = listOf(File(path).fileLike(name = "Image.jpg")))
}
findViewById<TextView>(R.id.image_desc).text = "Image contains: ${response?.text}"
} catch (e: Exception) {
findViewById<TextView>(R.id.image_desc).text = "Connection failed - please check fields are valid"
findViewById<TextView>(R.id.image_desc).text = e.toString()
}
} catch (e: UnknownHostException) {
findViewById<TextView>(R.id.image_desc).text = "Unknown host :("
e.printStackTrace()
} catch (e: IOException) {
findViewById<TextView>(R.id.image_desc).text = "IO exceptiion :("
e.printStackTrace()
} catch (e: Exception) {
findViewById<TextView>(R.id.image_desc).text = "Other exception :("
e.printStackTrace()
}
}
As soon as i send the image, image_desc textView's text change to Image contains: null. I'm sure the server isn't the problem, since when I test it with this python code:
import requests
url=...
files = {'file': open('./test/cat.jpg', 'rb')}
r=requests.post(url,files=files)
print (r.text)
I get the desired response after a short delay. I've tried turning sendImage to a suspend func and writing job.join() but that crashes the app. How should fix this?
Try next code:
val job = GlobalScope.launch(Dispatchers.Main) {
val postOperation = async(Dispatchers.IO) { // <- extension on launch scope, launched in IO dispatcher
// blocking I/O operation
post(SERVER_URL, files = listOf(File(path).fileLike(name = "Image.jpg")))
}
response = postOperation.await() // wait for result of I/O operation without blocking the main thread
findViewById<TextView>(R.id.image_desc).text = "Image contains: ${response?.text}"
}
Also add next line to app's build.gradle dependency:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'
Note that GlobalScope is discouraged to use, to launch a coroutine use an instance of CoroutineScope, or existing instance like viewModelScope or lifecycleScope.
UPDATE:
The correct approach would be to use lifecycleScope in Activity:
lifecycleScope.launch { // uses Dispatchers.Main context
val response = withContext(Dispatchers.IO) { // change context to background thread
// blocking I/O operation
post(SERVER_URL, files = listOf(File(path).fileLike(name = "Image.jpg")))
}
findViewById<TextView>(R.id.image_desc).text = "Image contains: ${response?.text}"
}