пятница, 29 апреля 2011 г.

Android. Реализация SendEmail

Отправить email из приложения. Это типичная задача, с которой сталкиваешься при разработке под Android. И делов то всего - скопипастить готовый код. Но есть несколько особенностей, которые легко пропустить... и потратить кучу времени в поисках ошибки.
Итак, типичный код, который гуляет на просторах интернета:
Intent it = new Intent(Intent.ACTION_SEND);   
it.putExtra(Intent.EXTRA_EMAIL, "me@abc.com");   
it.putExtra(Intent.EXTRA_TEXT, "The email body text");   
it.setType("text/plain");   
context.startActivity(Intent.createChooser(it, "Choose Email Client")); 
Сразу обратим внимание на строку "text/plain". Должно быть именно text/plain, а не plain/text. В половине примеров в интернете сделана эта ошибка. Результат - вместо диалогового окна со списком подходящих для отправки email приложений всегда вызывается GMail, без возможности выбрать приложение.

Кроме того, вариант "text/plain" поддерживается очень большим количеством приложений. Здесь и Google Reader, и Facebook и Bluetooth.. список может быть очень длинным. Чтобы в списке фигурировали только email-приложения, нужно указывать "text/xml".

Переходим к вызову Intent.createChooser. Функция Intent.createChooser выводит список приложений, способных обработать переданный в нее intent. При этом список приложений выводится всегда, галочки "Use by default for this action" в этом списке нет. В результате, пользователи вынуждены выбирать программу для отправки email каждый раз, что их крайне нервирует.

Можно легко обойтись без createChooser
 /*context.startActivity(Intent.createChooser(it, "Choose Email Client"));*/
 context.startActivity(it);
Диалог выбора приложений все так же будет показываться, но в нем появится галочка "Use by default for this action", позволяющая запомнить выбор (чтобы отменить то, что запомнили, нужно лезть в настройки, находить в них выбранное приложение и сбрасывать там флаг "Launch by default").

У createChooser есть еще один подводный камень. Если вы попытаетесь вызвать функцию из контекста виджета, то получите сообщение об ошибке - "Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?". Ясно, что надо выставить флаг FLAG_ACTIVITY_NEW_TASK. Важно только не ошибиться и выставить его у правильного intent - у того, что возвращает функция createChooser, а не того, который мы в нее передаем.

Итак, собираем все вместе и получаем функцию, реализующую перечисленные варианты работы:
/**
* Send email through one of email applications. 
* User selects app through dialog.
* @param bUseChooser - use or don't use application chooser. 
* Chooser asks user to select email application each time 
* without possibility to remember the choice.
* Checkbox "use by default for this action" is not available in chooser. 
* But it's available if chooser is turned off.  
* @param bUseTextPlain - use text/plain or text/xml. 
* If you use "text/xml" then the dialog shows email applications only. 
*/
public static void SendEmail(Context context
 , String srcEmail
 , String srcSubject
 , boolean bUseChooser
 , boolean bUseTextPlain
 , int resourceIdChooserMessage
) {
  Intent intent = new Intent(Intent.ACTION_SEND);
  intent.setType(! bUseTextPlain  
      ? "text/xml" //show email applications only
      : "text/plain"); 
  if (srcEmail != null) {
    intent.putExtra(Intent.EXTRA_EMAIL, new String[] { srcEmail });
  }
  if (srcSubject != null) {
    intent.putExtra(Intent.EXTRA_SUBJECT, new String[] { srcSubject });
  }
  if (bUseChooser) {
    //"use by default for this action" is not available here
    //user selects required application manually each time 
    Intent chooser_intent = Intent.createChooser(intent, context.getString(resourceIdChooserMessage));
    chooser_intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(chooser_intent);
  } else {
    context.startActivity(intent);
  }
}
...
//using without chooser
  SendEmail(context, "my@abc.com"
    , null
    , false
    , false
    , 0);

//using with chooser
  SendEmail(context, "my@abc.com"
    , null
    , true
    , false
    , R.string.select_email_app);


В большинстве случаев - сойдет.

Комментариев нет:

Отправить комментарий