I am receiving this error when trying to send multiple attachment via email:
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.SEND_MULTIPLE (has extras) }
I have Gmail and Outlook installed in my Phone.(Samsung device with Android 11). When sending a single attachment I am not facing any issue. I am using below intent to share multiple attachment over email
fun getIntent(
emailId: Array<String>,
subject: String,
body: String,
attachmentUri: ArrayList<Uri?>
): Intent? {
val text = ArrayList<String>()
text.add(body)
return Intent(Intent.ACTION_SEND_MULTIPLE).apply {
putExtra(Intent.EXTRA_EMAIL, emailId)
putExtra(Intent.EXTRA_SUBJECT, subject)
putExtra(
Intent.EXTRA_TEXT,
text
)
putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachmentUri)
}}
Above method is called in this way
val history: ArrayList<Uri?> = getHistory()
val intent = Utils.getIntent(
arrayOf(getString(R.string.email_id)),
subject,
getString(R.string.body),
history
)
intent?.let {
try {
startActivity(it)
} catch (e: Exception) {
}
}
String resource:
<string name="body">\u2022 Information:\n\u0020\u0020\u25E6Name: %s\n\u0020\u0020\u25E6Age: %s\n\u0020\u0020\u25E6%s\n</string>
What am I doing wrong?
According to the documentation on ACTION_SEND_MULTIPLE, you need to specify the MIME type of your content on your Intent, and you are not doing so.
Input: getType() is the MIME type of the data being sent. get*ArrayListExtra can have either a EXTRA_TEXT or EXTRA_STREAM field, containing the data to be sent. If using EXTRA_TEXT, you can also optionally supply EXTRA_HTML_TEXT for clients to retrieve your text with HTML formatting.
Multiple types are supported, and receivers should handle mixed types whenever possible. The right way for the receiver to check them is to use the content resolver on each URI. The intent sender should try to put the most concrete mime type in the intent type, but it can fall back to /* or / as needed.
e.g. if you are sending image/jpg and image/jpg, the intent's type can be image/jpg, but if you are sending image/jpg and image/png, then the intent's type should be image/*.
However, beyond that, it is entirely possible that a user will not have an app that supports ACTION_SEND_MULTIPLE for your requested MIME type. Make sure that you do something useful in your catch block for the try around your startActivity() call.
Related
I want to show all messaging apps installed by user in it's phone. The list I am expecting is like, WhatsApp, Facebook messenger, Viber, Slack, Skype, WeChat etc (If any installed). So, far I have tried getting all apps in Phone through this code:
val pm: PackageManager = context!!.packageManager
val i = Intent(Intent.ACTION_MAIN)
i.addCategory(Intent.CATEGORY_LAUNCHER)
val lst = pm.queryIntentActivities(i, 0)
for (resolveInfo in lst) {
Log.d(
"Test",
"New Launcher Found: " + resolveInfo.activityInfo.packageName
)
This only gives me Slack app but not other messaging apps. I have a feeling it has something to do with MIME types as mentioned in Google docs.
text/*, senders will often send text/plain, text/rtf, text/html, text/json
image/*, senders will often send image/jpg, image/png, image/gif
video/*, senders will often send video/mp4, video/3gp
but I don't know how to use this info. Any help would be appreciated. TIA!
add ACTION_SENDTOas action parameter while creating your intent and you should see the list of apps capable of handling messages/sms etc.
val intent = Intent(Intent.ACTION_SENDTO)
// get list of activities that can handle this type of intent
val lst = pm.queryIntentActivities(intent, 0)
Here pack name refers to - apps package name
fun appinstalled(){
var app_names = mutableListOf<String>()
val app_package = mutableListOf<String>()
val packagelist: MutableList<PackageInfo> = packageManager.getInstalledPackages(0)
var appname : String
var packname : String
for (i in packagelist.indices) {
val packageinfo : PackageInfo= packagelist[i]
appname=packageinfo.applicationInfo.loadLabel(packageManager).toString()
packname=packageinfo.packageName
app.add(appname)
app-package.add(packname)
}
this will the list of app installed in the user device as well as their package name . After this attach these list to the Listview and their adpater and all done.
Can't insert a new conversation to Telephony.Sms.Conversations.CONTENT_URI.
Keep getting a Caused by: java.lang.NullPointerException: Uri must not be null exception.
Even though the uri has a value of "content://sms/conversations".
Situation - logic flow
I receive an SMS message from an unknown number.
I insert a conversation for the unknown number (if one not found).
I insert the message and associate it with the newly created conversation.
Dev setup
For learning purposes, I am creating an Android SMS application with Kotlin.
Android Emulator with Pixel XL API 26.
The application I'm working on is set as the default SMS app.
Can successfully send, receive and insert (code below) individual messages.
createMessage() - works
Below is the working code I wrote to insert a message when the phone receives an SMS.
fun createMessage(
resolver: ContentResolver,
threadId: Number,
body: String,
sentByUs: Boolean
): Message? {
val messageType = if (sentByUs) Telephony.Sms.MESSAGE_TYPE_SENT else Telephony.Sms.MESSAGE_TYPE_INBOX
val values = ContentValues()
values.put(Telephony.Sms.THREAD_ID, threadId.toInt())
values.put(Telephony.Sms.BODY, body)
values.put(Telephony.Sms.TYPE, messageType)
val result = resolver.insert(Telephony.Sms.CONTENT_URI, values)
return this.getMessage(resolver, result)
}
createConversation() - doesn't work
Below is the code I'm working on which tries to insert a new conversation.
fun createConversation(
resolver: ContentResolver,
senderPhoneNumber: String,
latestMessageText: String,
latestMessageTimestamp: Long,
latestMessageIsOurs: Boolean,
latestMessageWasRead: Boolean
): Conversation? {
val wasRead = if (latestMessageWasRead) 1 else 0
val isOurs = if (latestMessageIsOurs) Telephony.Sms.Conversations.MESSAGE_TYPE_SENT else Telephony.Sms.Conversations.MESSAGE_TYPE_INBOX
val values = ContentValues()
values.put(Telephony.Sms.Conversations.ADDRESS, senderPhoneNumber)
values.put(Telephony.Sms.Conversations.BODY, latestMessageText)
values.put(Telephony.Sms.Conversations.DATE, latestMessageTimestamp)
values.put(Telephony.Sms.Conversations.TYPE, isOurs)
values.put(Telephony.Sms.Conversations.READ, wasRead)
// -------------------------------------------------------------------------
// ------ Throws java.lang.NullPointerException: Uri must not be null ------
// -------------------------------------------------------------------------
val result = resolver.insert(Telephony.Sms.Conversations.CONTENT_URI, values)
return this.getConversation(resolver, result)
}
While executing the resolver.insert() the application crashes with the following error message:
Caused by: java.lang.NullPointerException: Uri must not be null
With the debugger attached I can see that the uri does have a value.
Telephony.Sms.Conversations.CONTENT_URI is "content://sms/conversations"
How does Google do it?
Found out that Google open sources its common Android apps.
Here's the code for the Messaging application:
https://android.googlesource.com/platform/packages/apps/Messaging/
While analyzing DatabaseHelper.java I came to the conclusion that they create a whole separate database from scratch.
And then work with that troughout the lifetime of the application.
Which confused me even more - why don't they use resolver.insert()?
I may be wrong, the program was overwhelming for a new guy like me.
Question
If Telephony.Sms.Conversations.CONTENT_URI has a value of "content://sms/conversations", why do I get the exception?
To investigate such issue you need to turn off logcat filtering for your app. Then you'll find an SQLiteException that will help find out what's actually wrong.
The NPE refers to the fact that the insert failed and return a null Uri instead of the appropriate Uri path of the new element.
The HTML body of an email opened/created from my Android application does not show any styling (CSS). Is there any way to make the CSS appear?
Firstly; I've tried adding a complete HTML template as well as just one html tag (H1). None of those show the CSS as expected.
Secondly; I've tried putting the CSS inline as well as in the style tag in the head section of my HTML document. Again, without any succes.
Note: The Kotlin code I provided opens the email client (Outlook) correctly and show the HTML code. However, it seems no styling has been added.
// Kotlin code
// Send email function
fun sendEmail(activity: Activity, TO: Array<String>?, CC: Array<String>?, Subject: String, ContentText: String?, ContentHtml: String?) {
Log.i("Sending email", "")
val emailIntent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:"))
emailIntent.putExtra(Intent.EXTRA_EMAIL, TO)
emailIntent.putExtra(Intent.EXTRA_CC, CC)
emailIntent.putExtra(Intent.EXTRA_SUBJECT, Subject)
emailIntent.putExtra(Intent.EXTRA_TEXT, ContentText)
emailIntent.putExtra(Intent.EXTRA_HTML_TEXT, ContentHtml)
try {
activity.startActivity(Intent.createChooser(emailIntent, "Send mail..."))
//finish()
Log.i("Finished sending email.", "")
} catch (ex: android.content.ActivityNotFoundException) {
Toast.makeText(activity, "There is no email client installed.", Toast.LENGTH_SHORT).show()
}
}
// Calling the function with some values
val TO = null
val CC = null
val Subject = "Your subject"
val ContentText = "Email message goes here"
val ContentHtml = "<h1 style=\"color:red; margin: 15px;\">Hello World</h1>"
sendEmail(this#MainActivity, TO, CC, Subject, ContentText, ContentHtml)
While using the code above I received a black h1 header in my email. No styling has been added. Is there any way to make the styling/CSS visible in emails created from Kotlin?
Have you tried to write your ContentHtml part inside ` (tilde) instead of double quotes " "? May be this will help.
I have seen many examples in Delphi and not one a C ++ builder. I tried to recreate the code in C ++, but it flies only exception. How to use the Intent to ะก++?
void __fastcall TForm1::Button1Click(TObject *Sender)
{
callEmail("russia#gmail.com", "Application");
}
//---------------------------------------------------------------------------
void TForm1::callEmail(const String address, const String Subject){
JIntent* intent;
TJIntent* intentTwo;
intent = intentTwo->Create();
intent->setAction(intentTwo->JavaClass->ACTION_SEND);
intent->setFlags(intentTwo->JavaClass->FLAG_ACTIVITY_NEW_TASK);
intent->putExtra(intentTwo->JavaClass->EXTRA_EMAIL, StringToJString(address));
intent->putExtra(intentTwo->JavaClass->EXTRA_SUBJECT, StringToJString(Subject));
intent->setType(StringToJString('vnd.android.cursor.dir/email'));
SharedActivity()->startActivity(intent);
}
I thnik, maybe I think maybe something needs to change in androidmanifest or user-permission?
Your code is crashing because you are not constructing the Intent object correctly.
Create() is a constructor in Delphi. intent := TJIntent.Create in Delphi would be intent = new TJIntent in C++.
Also, Embarcadero uses interfaces for its iOS/Android bridge frameworks, so you should use the provided DelphiInterface<T> typedefs, such as _di_JIntent instead of JIntent* directly.
Also, JavaClass (and OCClass in iOS) is a static class property. You do not need an object instance to access it, just the class type.
Also, C++ uses single-quotes for character literals and double-quotes for string literals, whereas Delphi uses single-quotes for both. 'vnd.android.cursor.dir/email' in C++ is not a string literal, it is a multi-byte character literal instead, which is not what you want here. Use double-quotes instead.
Also, EXTRA_EMAIL must be expressed as an array of strings.
Try something more like this:
void TForm1::callEmail(const String address, const String Subject)
{
_di_JIntent intent;
intent = new TJIntent; // or: intent = TJIntent::JavaClass->init();
intent->setAction(TJIntent::JavaClass->ACTION_SEND);
// or: intent = TJIntent::JavaClass->init(TJIntent::JavaClass->ACTION_SEND);
intent->setFlags(TJIntent::JavaClass->FLAG_ACTIVITY_NEW_TASK);
TJavaObjectArray__1<_di_JString> *Recipients = new TJavaObjectArray__1<_di_JString>(1);
Recipients->Items[0] = StringToJString(address);
intent->putExtra(TJIntent::JavaClass->EXTRA_EMAIL, Recipients);
intent->putExtra(TJIntent::JavaClass->EXTRA_SUBJECT, StringToJString(Subject));
intent->setType(StringToJString(L"vnd.android.cursor.dir/email"));
SharedActivity()->startActivity(intent);
}
Now, that said, you really should not be using vnd.android.cursor.dir/email as the intent's MIME type. Use message/rfc822 instead, or even plain/text. But those do not limit the intent to just email clients, other apps might also support those types. To send an email using only a true email client, use ACTION_SENDTO with a mailto: URI instead. For 1 recipient, you can put the address directly in the URI and not use EXTRA_EMAIL at all. For 2+ recipients, use a mailto: URI with no address in it and use EXTRA_EMAIL for the addresses. This is mentioned in the Android documentation:
Common Intents | Email
For example:
void TForm1::callEmail(const String address, const String Subject)
{
_di_JIntent intent;
intent = new TJIntent; // or: intent = TJIntent::JavaClass->init();
intent->setAction(TJIntent::JavaClass->ACTION_SENDTO);
intent->setData(StrToJURI(L"mailto:" + address));
// or: intent = TJIntent::JavaClass->init(TJIntent::JavaClass->ACTION_SENDTO, StrToJURI(L"mailto:" + address));
intent->setFlags(TJIntent::JavaClass->FLAG_ACTIVITY_NEW_TASK);
intent->putExtra(TJIntent::JavaClass->EXTRA_SUBJECT, StringToJString(Subject));
intent->setType(StringToJString(L"message/rfc822"));
SharedActivity()->startActivity(intent);
}
I would know one thing about SMS...
I know that is possible read SMS in Android, but I don't know how I can read "Unread SMS", and in particular how to accede to each field like "sender", "SMS", "date", "time", "text".
What that i must do is: when a sms is received, an application "reads" the message and stores all the information in a data structure.
IMPORTANT:
Another question: is it possible read "Unread SMS" in WhatsApp or other IM application (for example facebook messenger) ?
And in this case how I can accede to each field ?
Regards
Reading Phone SMS
Check this out How can I read SMS messages from the device programmatically in Android?
Reading Facebook messages
For facebook you need to implement their API. See this Read Messages in Facebook
Reading Whatsapp messages
Option 1
Whatsapp did not publish any official APIs.
There's this open source API for communicating with whatsapp, it's not official and might stop working if Whatsapp update their protocols.
https://github.com/venomous0x/WhatsAPI
Regarding the legality of using this or other non-official API, it depends on the service agreement that you agreed to with Whatsapp. Read it and see if they frown upon using their communication protocols with clients other than theirs. My guess would be they do not allow it.
Option 2
WhatsApp makes a chat backup everyday at 4 AM on your SD Card. This is a single database encrypted with an AES key. Since it is on the external storage, your app can read it if you have access to the external storage.
You can easily decrypt this database (there is a paper available on this online).
However, this will only give you updated chats once every 24 hours.
If you want more realtime updates, your device has to be rooted, and you will need a completely new parser, as on the internal storage the chats are stored in more than one database.
Option 3
Read this SO question Get all messages from Whatsapp
NOTE: I am not sure about the Whatsapp stuff. It's just a compilation of various posts together.
The easiest function
To read the sms I wrote a function that returns a Conversation object:
class Conversation(val number: String, val message: List<Message>)
class Message(val number: String, val body: String, val date: Date)
fun getSmsConversation(context: Context, number: String? = null, completion: (conversations: List<Conversation>?) -> Unit) {
val cursor = context.contentResolver.query(Telephony.Sms.CONTENT_URI, null, null, null, null)
val numbers = ArrayList<String>()
val messages = ArrayList<Message>()
var results = ArrayList<Conversation>()
while (cursor != null && cursor.moveToNext()) {
val smsDate = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.DATE))
val number = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.ADDRESS))
val body = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.BODY))
numbers.add(number)
messages.add(Message(number, body, Date(smsDate.toLong())))
}
cursor?.close()
numbers.forEach { number ->
if (results.find { it.number == number } == null) {
val msg = messages.filter { it.number == number }
results.add(Conversation(number = number, message = msg))
}
}
if (number != null) {
results = results.filter { it.number == number } as ArrayList<Conversation>
}
completion(results)
}
Using:
getSmsConversation(this){ conversations ->
conversations.forEach { conversation ->
println("Number: ${conversation.number}")
println("Message One: ${conversation.message[0].body}")
println("Message Two: ${conversation.message[1].body}")
}
}
Or get only conversation of specific number:
getSmsConversation(this, "+33666494128"){ conversations ->
conversations.forEach { conversation ->
println("Number: ${conversation.number}")
println("Message One: ${conversation.message[0].body}")
println("Message Two: ${conversation.message[1].body}")
}
}