How send email Android with c++ builder? - android

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);
}

Related

ActivityNotFoundException when trying to send multiple attachments via emails

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.

How to make android.net.Uri encode & between query parameters to %26

I have a Android app that needs to launch a web brower with a URL containing a query string. I build my Uri like this:
Uri uri = builder.scheme("https")
.authority("ids.example.com")
.appendPath("account")
.appendPath("login")
.appendQueryParameter("client_id", "seglaren")
.appendQueryParameter("scope", "openid email name")
.build();
and pass it to the browser using:
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
This launches the browser with the following URL:
https://ids.example.com/account/login?client_id=seglaren&scope=openid%20email%20name
The problem here is that the server I am calling does not accept this URL: it requires the separator between the query parameters to be encoded to "%26" instead of just "&". So it would need to be:
https://ids.example.com/account/login?client_id=seglaren%26scope=openid%20email%20name
How do I fix this?
Instead of .appendQueryParameter() you can use .encodedQuery().
encodedQuery() will be treated as if it is already encoded, thus not encoding it again. So you may insert your own string as you wish like in the example below.
String params = "client_id=seglaren%26scope=openid%20email%20name";
Uri uri = new Uri.Builder().scheme("https")
.authority("ids.artdatabanken.se")
.appendPath("account")
.appendPath("login")
.encodedQuery(params)
.build();
You may use string concatenation or a StringBuilder to make the String params dynamic if you don't want to keep it hardcoded.
Result
"https://ids.artdatabanken.se/account/login?client_id=seglaren%26scope=openid%20email%20name"
Note that androids Uri.Builder is doing the correct thing by adding &to the parameter. So the API you're using probably has a bug if it requires %26.

NFC Read Tag SMS Service

I have written a program in which I am trying to write and read NFC tag, which may help user in sending message. I have successfully written to the tag but whenever I am trying to read, the Tags app shows vnd.android.nfc://ext/nfclab.com:smsService but does not allow me to send the message.
WriteSmsActivity.java:
#Override
public void onNewIntent(Intent intent) {
Log.i("Foreground dispatch", "Discovered tag with intent: " + intent);
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
String externalType = "nfclab.com:smsService";
String smsNumber = smsNumberEditText.getText().toString();
String smsBody = smsBodyEditText.getText().toString();
String urlAddress = "sms:"+smsNumber+"?body="+smsBody;
NdefRecord extRecord = new NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE, externalType.getBytes(), new byte[0], urlAddress.getBytes());
NdefMessage newMessage = new NdefMessage(new NdefRecord[] { extRecord});
writeNdefMessageToTag(newMessage, tag);
}
}
Android displays the unrecognized NFC Forum external type nfclab.com:smsService (note that only lower-case letters should be used for external type names, see my answer here), because you stored that record type on your tag. That type is a custom type created by nfclab.com (for rather artificial examples in their book that won't work without a customized reader app) and by no means standardized. Consequently, Android does not know what it should do with that record.
The standard way to store ready-made SMS messages on NFC tags is the sms: URI scheme. Hence, you would typically create a URI record containing your sms: URI:
String smsUri = "sms:" + phone_number + "?body=" + text_message;
NdefRecord smsUriRecord = NdefRecord.createUri(smsUri);
Since Android 4.1 (or so), such records should be handled automatically by Android and will permit you to open the sms: URI in the default SMS app.

Is there a way to create an ACTION_NDEF_DISCOVERED intent from code

For an app i'm writing i am wondering if it is possible to create an ACTION_NDEF_DISCOVERED intent from code. Normally this intent is created by the system when reading an ndef formatted tag. It contains a parcableextra of the type Tag.
The creation of the intent is probably easy but can you also create a Tag from code or is that not supported by the class as i suppose.
The goal is to broadcast a virtual tag with an ndef record to the system that then can be handled by an app that calls on the ACTION_NDEF_DISCOVERED intent.
You could get a mock tag object instance using reflection. Something like this should work:
NdefMessage ndefMsg = ...;
Class tagClass = Tag.class;
Method createMockTagMethod = tagClass.getMethod("createMockTag", byte[].class, int[].class, Bundle[].class);
final int TECH_NFC_A = 1;
final int TECH_NDEF = 6;
final String EXTRA_NDEF_MSG = "ndefmsg";
final String EXTRA_NDEF_MAXLENGTH = "ndefmaxlength";
final String EXTRA_NDEF_CARDSTATE = "ndefcardstate";
final String EXTRA_NDEF_TYPE = "ndeftype";
Bundle ndefBundle = new Bundle();
ndefBundle.putInt(EXTRA_NDEF_MSG, 48); // result for getMaxSize()
ndefBundle.putInt(EXTRA_NDEF_CARDSTATE, 1); // 1: read-only, 2: read/write
ndefBundle.putInt(EXTRA_NDEF_TYPE, 2); // 1: T1T, 2: T2T, 3: T3T, 4: T4T, 101: MF Classic, 102: ICODE
ndefBundle.putParcelable(EXTRA_NDEF_MSG, ndefMsg);
Tag mockTag = (Tag)createMockTagMethod.invoke(null,
new byte[] { (byte)0x12, (byte)0x34, (byte)0x56, (byte)0x78 },
new int[] { TECH_NFC_A, TECH_NDEF },
new Bundle[] { null, ndefBundle });
The problem with this is that you will not be able to connect to this tag. Consequently, all methods of the Ndef object (that you can get from that mock Tag instance) that require IO operations with a real tag or a real tag that is registered with the NFC service will fail. Specifically, only
getCachedNdefMessage(),
getMaxSize(),
getType(),
isWritable(), and
getTag()
will work.
So pretty much the same functionality would be available if you do not pass a Tag object as part of the NDEF_DISCOVERED intent and instead just use the EXTRA_NDEF_MESSAGES intent extra.

Qt Android Extras Call Class

Say I have a Java class,
public static String helloWorld() {
return "hello world!";
}
How, in Qt, do I get what this function returned? The example for notifications has the following:
QAndroidJniObject javaNotification = QAndroidJniObject::fromString(m_notification);
QAndroidJniObject::callStaticMethod<void>("org/qtproject/example/notification/NotificationClient",
"notify",
"(Ljava/lang/String;)V",
javaNotification.object<jstring>());
Should by method descriptor be ()Ljava/lang/String;? What should be in the chevrons after callStaticMethod?
Edit. Fixed and I don't know how. I didn't have the chevrons, and the descriptor was correct.
QAndroidJniObject string = QAndroidJniObject::callStaticObjectMethod<jstring>("com/your/project/YourClass", "helloWorld");
Is equivalent to:
QAndroidJniObject string = QAndroidJniObject::callStaticObjectMethod("com/your/project/YourClass", "helloWorld", "()Ljava/lang/String;");
For trivial signatures, that is, signatures that takes no arguments and returns one of the know jni types, you can write the short version by supplying the template type.

Categories

Resources