My app sends an activation code to email during user's registration process, but for an unknown -to me- reason in some Chinese devices is not working and I'm not being able to get a log of the Exception.
This is where I send the email:
private void getAwsResult_getAwsUser(String strResult)
{
try{
CommunityUserDTO communityUser = CommunityUserService.deserializeUser(strResult);
if(communityUser==null){ //email don't exist in database
registerResult.setValue(new Pair<>(null, Enum.RegisterResult.SENDINGEMAIL));
CreateUserUseCase.setActivationCode(this.user);
sendConfirmationEmail(this.user.getEmail(), this.user.getActivationCode());
}else{
registerResult.setValue(new Pair<>(null, Enum.RegisterResult.EMAILALREADYREGISTERED));
}
}catch(Exception ex)
{
ExceptionHandler.logException(ex);
}
}
private void sendConfirmationEmail(String recipient, int activationCode)
{
String jsonEmail = SendEmailUseCase.
generateJsonConfirmationEmail(recipient, activationCode);
TaskRunner taskRunner = new TaskRunner();
taskRunner.executeAsync(new SendEmailTask(jsonEmail), this::getAwsResult_sendEmail);
}
public class SendEmailTask implements Callable<String>
{
private final String jsonEmail;
public SendEmailTask(String jsonEmail)
{
this.jsonEmail = jsonEmail;
}
#Override
public String call()
{
return TMEmail.send(jsonEmail);
}
}
This is a jsonEmail input parameter for sendEmail method example:
{
"subject":"Your activation code.",
"message":"Your activation code is: xxxxxx",
"send_to":"xxx#gmail.com",
"send_from":"contact#xxx.com",
"smtp_server":"xxx.amazonaws.com",
"smtp_user":"xxx",
"smtp_pass":"xxx",
"smtp_useauth":"true",
"smtp_port":"25",
"center_text":"true"
}
public static String send(String jsonEmail)
{
String result = Enum.Result.OK;
JSONObject oJsonEmail = TMJson.str2JSON(jsonEmail);
try {
String subject = oJsonEmail.getString(Enum.Email.SUBJECT);
String message = oJsonEmail.getString(Enum.Email.MESSAGE);
String sendFrom = oJsonEmail.getString(Enum.Email.SEND_FROM);
String sendTo = oJsonEmail.getString(Enum.Email.SEND_TO);
String smtpServer = oJsonEmail.getString(Enum.Email.SMTP_SERVER);
String smtpPort = oJsonEmail.getString(Enum.Email.SMTP_PORT);
String smtpUseAuth = oJsonEmail.getString(Enum.Email.SMTP_USEAUTH);
boolean centerBodyText = Boolean.parseBoolean(oJsonEmail.getString(Enum.Email.CENTER_TEXT));
final String smtpUser = oJsonEmail.getString(Enum.Email.SMTP_USER);
final String smtpPass = oJsonEmail.getString(Enum.Email.SMTP_PASS);
Properties props = new Properties();
props.put("mail.smtp.host", smtpServer);
props.put("mail.smtp.port", smtpPort);
props.put("mail.smtp.auth", smtpUseAuth);
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.from.alias", "My App Name");
Session session = Session.getInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(smtpUser, smtpPass);
}
});
message = TMJson.decodeEmail(message);
message = format(message, centerBodyText);
Message oMessage = new MimeMessage(session);
oMessage.setFrom(new InternetAddress(sendFrom, session.getProperty("mail.from.alias"), "UTF-8"));
oMessage.setRecipients(Message.RecipientType.TO, InternetAddress.parse(sendTo));
oMessage.setSubject(subject);
oMessage.setContent(message, "text/html; charset=iso-8859-2");
Transport.send(oMessage);
}catch (MessagingException | UnsupportedEncodingException | JSONException e) {
result = Enum.Result.KO;
ExceptionHandler.logException(e);
}
return result;
}
I've tested this in my Android mobile and tablet, and in many other devices and it's working just fine, the email is sent and the user receives the activation code, but in China, where we are still testing the beta version the tester is not receiving the email.
I know the app is crashing before sending the email, but cannot figure out why, and if you notice, I hace a try/catch with this ExceptionHandler.logException(e); that sends a report of the crash to my email, but it's also not working, so I don't know what to do.
I contacted AWS support team to check if there is any possibility to see any SES logs, but has no response yet.
Any ideas on how could I get closer to the real exception? Still cannot even see it in the Google Play Developer Console crashes report section.
Ok, luckily I found the issue myself. Nothing to do with Chinese devices, but with Android version.
I was testing my app in Android 10 devices (the only ones I have) and they were testing in an Android 11 device.
This link solved my problem:
Android Studio VerifyError rejecting class text_plain from JavaMail API
And this is the solution: Add this in build.gradle
android {
...
packagingOptions {
exclude 'META-INF/NOTICE.md'
exclude 'META-INF/LICENSE.md'
}
}
dependencies {
implementation 'com.sun.mail:android-mail:1.6.6'
implementation 'com.sun.mail:android-activation:1.6.6'
...
}
Hope I'm helping anyone else having problems sending emails.
Related
I have an app connected with Azure backend. I created a login and some api calls 2 months ago. They worked fine until a few days ago and then it starts to fail "sometimes".
The login log onFailure says: Error while authenticating user
The callback log onFailure says: Error while processing request
And the cause of both says : stream was reset: PROTOCOL_ERROR
This post is to similar to this but didn't work.
Some code here:
LoginFragment.java
private void login(String email, String password){
loginProgressBar.setVisibility(View.VISIBLE);
try {
JsonObject params = new JsonObject();
params.addProperty("Username", email);
params.addProperty("Password", password);
ListenableFuture<MobileServiceUser> listenable = Client.logIn(getContext(), params);
Futures.addCallback(listenable, new FutureCallback<MobileServiceUser>() {
#Override
public void onSuccess(MobileServiceUser mobileServiceUser) {
loginProgressBar.setVisibility(View.GONE);
SharedPreferences settings = getActivity().getSharedPreferences(Client.MS_USER,0);
SharedPreferences.Editor editor = settings.edit();
Client.clientId = mobileServiceUser.getUserId();
Client.token = mobileServiceUser.getAuthenticationToken();
editor.putString(Client.MS_USER_ID, Client.clientId);
editor.putString(Client.MS_AUTH_TOKEN, Client.token);
editor.apply();
Client.getInstance(getContext()).setCurrentUser(mobileServiceUser);
Intent i = new Intent(getContext(), MainActivity.class);
startActivity(i);
}
#Override
public void onFailure(Throwable t) {
loginProgressBar.setVisibility(View.GONE);
Throwable t2 = t.getCause();
Throwable t3 = t2.getCause();
Log.e("LoginFail", t.getMessage());
Log.e("LoginFail", t2.getMessage());
if(t3 != null){
Log.e("LoginFail", t3.getMessage());
}
Toast.makeText(getContext(), getResources().getString(R.string.bad_login), Toast.LENGTH_LONG).show();
}
}, MoreExecutors.directExecutor());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
Client.java
public class Client {
public static final String MS_USER = "MS_USER";
public static final String MS_USER_ID = "MS_USER_ID";
public static final String MS_AUTH_TOKEN = "MS_AUTH_TOKEN";
public static String clientId;
public static String token;
private static MobileServiceClient instance = null;
public static MobileServiceClient getInstance(Context context) {
if (instance ==null){
try {
instance = new MobileServiceClient(Env.AZURE_URL, context);
instance.setAndroidHttpClientFactory(() -> {
OkHttpClient client = new OkHttpClient();
client.setReadTimeout(20, TimeUnit.SECONDS);
client.setWriteTimeout(20, TimeUnit.SECONDS);
return client;
});
} catch (MalformedURLException e) {
e.printStackTrace();
}
} else{
instance.setContext(context);
}
return instance;
}
public static ListenableFuture<MobileServiceUser> logIn(Context context, JsonObject parameters) throws MalformedURLException {
String deviceID = "gcm:" + Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
parameters.addProperty("device_id", deviceID);
parameters.addProperty("device_dateTime", Env.DATE_FORMAT.format(new Date()));
parameters.addProperty("device_timeZone", API.getTimezone());
parameters.addProperty("device_language", Env.LANGUAGE);
parameters.addProperty("app", Env.APP_NAME);
return getInstance(context).login("auth", parameters);
}
public static ListenableFuture<JsonElement> callApi(Context context, String apiName, JsonObject parameters, String httpMethod){
if(httpMethod.equals("POST")){
String deviceID = "gcm:" + Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
parameters.addProperty("user_id", Client.clientId);
parameters.addProperty("device_id", deviceID);
parameters.addProperty("device_dateTime", Env.DATE_FORMAT.format(new Date()));
parameters.addProperty("device_timeZone", API.getTimezone());
parameters.addProperty("device_language", Env.LANGUAGE);
parameters.addProperty("app", Env.APP_NAME);
parameters.addProperty("role", "Patient");
return getInstance(context).invokeApi(apiName, parameters, httpMethod, null);
} else {
return getInstance(context).invokeApi(apiName, null, httpMethod, null);
}
}
This is probably related to an issue in Azure App Service that is weirdly enough not reported on the public Azure status page.
The message that affected Azure client received was (quoted from the link above):
Starting at 02:00 UTC on 3 Apr 2018, you have been identified as a
customer using App Services who may have received connection failure
notifications when using Android apps with older HTTP clients or
desktop browsers using cross-site scripting calls. Engineers have
identified an issue with a recent deployment and are investigating
mitigation options. Customers experiencing this issue can
self-mitigate by updating the site config setting "http20Enabled" to
false via resources.azure.com. Instructions on how to update site
config can be found here:
https://azure.microsoft.com/en-us/blog/azure-resource-explorer-a-new-tool-to-discover-the-azure-api/
Go to https://resources.azure.com/
Make sure you are in Read/Write mode by clicking in the option to the
left of your name.
Find the affected site and browse to Config > Web:
https://resources.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/web
Change the property: "http20Enabled": from true to false by clicking
in Edit properties, Update to “false” and then clicking PUT to save
change.
If you have tried these steps and are continuing to experience issues
with your App Service, please create a technical support ticket to
further troubleshoot: aka.ms/azsupt. This message will be closed in 7
days.
I'm using the oauth2-essentials library because it was one of the recommended oauth libraries on the uber developers site, and it has been recently updated.
My code looks like this:
executor = new HttpUrlConnectionExecutor();
OAuth2AuthorizationProvider provider = new BasicOAuth2AuthorizationProvider(
URI.create("https://login.uber.com/oauth/v2/authorize"),
URI.create("https://login.uber.com/oauth/v2/token"),
new Duration(1,0,3600) /* default expiration time in case the server doesn't return any */);
OAuth2ClientCredentials credentials = new BasicOAuth2ClientCredentials(
getString(R.string.uberClientId), getString(R.string.uberSecret));
uberOAuthClient = new BasicOAuth2Client(
provider,
credentials,
new LazyUri(new Precoded("my redirect url")) /* Redirect URL */);
generateUberOAuthToken("my phone number", "my password");
}
public void generateUberOAuthToken(final String uName, final String password){
new Thread() {
public void run() {
try {
uberOAuthToken = new ResourceOwnerPasswordGrant(
uberOAuthClient, new BasicScope("profile"), uName, password).accessToken(executor);
}
catch (Exception e){
e.printStackTrace();
}
}
}.start();
}
The exception: org.dmfs.httpessentials.exceptions.UnauthorizedException: Authentication at 'https://login.uber.com/oauth/v2/token' failed. is always thrown. I've tried removing the redirect url so it should use the default from my dashboard, and I've tried added 1 and/or dashes to the phone number. The exception is always thrown when I request an access token. I know my account is setup correctly because this works fine in iOS. I feel like I must be missing something obvious. Anyone else run into this issue?
The token api did not recognize my phone number as a valid login parameter. It worked when I used my email instead.
I am trying to subscribe events from IBM IoT platform.
The example I tested is from an IBM blog: https://www.ibm.com/developerworks/library/iot-mobile-phone-iot-device-bluemix-apps-trs/. In this link, the codes can be downloaded.
With this example, the Android app can successfully connect and publish events to the IBM IoT platform, but when I tried "subscribe", I always receive the error "Connection lost". What causes this problem? Thank you!
The relevant codes are:
public IMqttToken subscribeToEvent(String deviceType, String deviceId, String event, String format, int qos, Object userContext, IMqttActionListener listener) throws MqttException {
String eventTopic = "iot-2/type/" + deviceType + "/id/" + deviceId + "/evt/" + event + "/fmt/"+format;
return subscribe(eventTopic, qos, userContext, listener);
}
public static void subscribeEvent(Context context,String event) {
Log.v(TAG, ".subscribeEvent() entered")
try {
MyIoTActionListener listener = new MyIoTActionListener(context, Constants.ActionStateStatus.SUBSCRIBE);
IoTClient iotClient = IoTClient.getInstance(context);
String deviceType = "Android";
String deviceId = "...";
iotClient.subscribeToEvent(deviceType, deviceId, event, "json", 0, context,listener);
} catch (MqttException e) {
Log.d(TAG, ".SubscribeEvent() received exception on SubscribeEvent()");
}
}
The connection URL is:
String connectionURI = "tcp://" + this.getOrganization() + ".messaging.internetofthings.ibmcloud.com:1883";
ssl somehow cannot work.
This problem has been solved.
Two concepts have to be clarified: device client and application client.
A device client can only publish events and subscribe commands. In the question, I subscribe events with a device client, the IoT platform cannot response and returns the error "Connection Lost". The correct way to code is:
public IMqttToken subscribeToCommand(String command, String format, int qos, Object userContext, IMqttActionListener listener) throws MqttException {
String commandTopic = getCommandTopic(command, format);
return subscribe(commandTopic, qos, userContext, listener);
}
public static String getCommandTopic(String command, String format) {
return "iot-2/cmd/" + command + "/fmt/json";
}
public static void subscribeCommand(Context context, String command) {
try {
MyIoTActionListener listener = new MyIoTActionListener(context, Constants.ActionStateStatus.SUBSCRIBE);
IoTClient iotClient = IoTClient.getInstance(context);
iotClient.subscribeToCommand(command, "json", 0, context,listener);
} catch (MqttException e) {
Log.d(TAG, ".SubscribeCommand() received exception on SubscribeEvent()");
}
}
The example provided by IBM contains functions like subscribeEvent and publishCommand for a device client. These functions make me confused.
If you want to publish commands and subscribe events, an application client need to be created. The method to create an application client is:
import com.ibm.iotf.client.app.ApplicationClient;
Properties appProperties = new Properties();
appProperties.put("id",applicationId);
appProperties.put("Organization-ID",organizationId);
appProperties.put("Authentication-Method","apikey");
appProperties.put("API-Key",apiKey);
appProperties.put("Authentication-Token",token);
appClient = new ApplicationClient(appProperties);
appClient.connect();
See more about application client at https://github.com/ibm-watson-iot/iot-java/blob/master/src/main/java/com/ibm/iotf/client/app/ApplicationClient.java
I used javamail api in my android project and parse String in "mail subject" and "mail content". In my gmail there are many mail boxes such as travel, job, photo.... I need go through all mail boxes and search mails in my conditions. But I found when call getData() it will login gmail and make connection again.
My question are
1.How can it Keep connection in android?
2.How can I search all the mail boxes in the same time?
Thanks for help.
private String downloadUrl () throws IOException {
receiveMail("username", "password");
return null;
}
receiveMail part
private static List<Message> receiveMail(String username, String Password) {
try {
Properties props = System.getProperties();
props.setProperty("mail.store.protocol", IMAPS_PROTOCOL);
Session session = Session.getDefaultInstance(props, null );
session.setDebug(false);
final Store store = session.getStore(IMAPS_PROTOCOL);
store.connect(IMAPS_MAIL_HOTS , username, Password);
getData(store, "travel");
getData(store, "job");
getData(store, "photo");
} catch(Exception ex) {
ex.printStackTrace();
}
return null;
}
If you want to reuse a single connection for each folder, you need to be sure to close the folder before opening the next folder.
I have been trying to write an app that periodically parses the contents of gmail messages. I have been through the JavaMail FAQ and I have looked at a number of examples in the JavaMail download package but have been unable to get this to work. The code below currently causes the following gmail error:
Host is unresolved: imaps.gmail.com:993
I have also tried imap.gmail.com:143 but get:
Host is unresolved: imap.gmail.com:143
Any help or advice would be greatly appreciated. GMailReader is the class I am using to try and return gmail imap messages:
public class GMailReader extends javax.mail.Authenticator {
private String mailhost = "imaps.gmail.com";
private String user;
private String password;
private Session session;
public GMailReader(String user, String password) {
this.user = user;
this.password = password;
Properties props = new Properties();
props.setProperty("mail.transport.protocol", "imaps");
props.setProperty("mail.imaps.host", mailhost);
props.put("mail.imaps.auth", "true");
props.put("mail.imaps.port", "993");
props.put("mail.imaps.socketFactory.port", "993");
props.put("mail.imaps.socketFactory.class",
"javax.net.ssl.SSLSocketFactory");
props.put("mail.imaps.socketFactory.fallback", "false");
props.setProperty("mail.imaps.quitwait", "false");
session = Session.getDefaultInstance(props, this);
}
public synchronized Message[] readMail() throws Exception {
try {
Store store = session.getStore("imaps");
store.connect("imaps.gmail.com", user, password);
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
Message[] msgs = folder.getMessages(1, 10);
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
folder.fetch(msgs, fp);
return msgs;
} catch (Exception e) {
Log.e("readMail", e.getMessage(), e);
return null;
}
}
}
I found an example here that was helpful. My error was the use of "mail.transport.protocol" rather than "mail.store.protocol."
hereafter a corrected version of
public class GMailReader extends javax.mail.Authenticator {
private static final String TAG = "GMailReader";
private String mailhost = "imap.gmail.com";
private Session session;
private Store store;
public GMailReader(String user, String password) {
Properties props = System.getProperties();
if (props == null){
Log.e(TAG, "Properties are null !!");
}else{
props.setProperty("mail.store.protocol", "imaps");
Log.d(TAG, "Transport: "+props.getProperty("mail.transport.protocol"));
Log.d(TAG, "Store: "+props.getProperty("mail.store.protocol"));
Log.d(TAG, "Host: "+props.getProperty("mail.imap.host"));
Log.d(TAG, "Authentication: "+props.getProperty("mail.imap.auth"));
Log.d(TAG, "Port: "+props.getProperty("mail.imap.port"));
}
try {
session = Session.getDefaultInstance(props, null);
store = session.getStore("imaps");
store.connect(mailhost, user, password);
Log.i(TAG, "Store: "+store.toString());
} catch (NoSuchProviderException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MessagingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public synchronized Message[] readMail() throws Exception {
try {
Folder folder = store.getFolder("Inbox");
folder.open(Folder.READ_ONLY);
/* TODO to rework
Message[] msgs = folder.getMessages(1, 10);
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
folder.fetch(msgs, fp);
*/
Message[] msgs = folder.getMessages();
return msgs;
} catch (Exception e) {
Log.e("readMail", e.getMessage(), e);
return null;
}
}
}
Bye
I see that the GmailReader concept very usefull and well designed in accordance whith the GmailSender example showed here:
Sending Email in Android using JavaMail API without using the default/built-in app
But Any news, on the error asked below ? And implementation of the proposition of JackN ?
Best Regards
SkN
After a huge amount of trial, error and googling , snakeman's edition of this answer provided the workable example I needed for a gmail reader;
However others should be aware (if using later versions of the Android SDK) of Manifest permission requirements and the need to use asyncTask to move potentially long-running tasks out of the main UI thread), both of which are mentioned in this SMTP example
I should also mention that if, like me, you intend to also implement an smtp sending class, I have seen somewhere a discussion suggesting that session.getInstance should be used in place of session.getDefaultInstance.