вторник, 31 июля 2012 г.

PhoneGap: используем чужие плагины и пишем свои

Предыдущий мой пост о PhoneGap был рассчитан на начинающих разработчиков для мобильных устройств. Теперь давайте обсудим, какую пользу из этого фреймворка могут извлечь те, кто уже умеет делать "настоящие" Android (IOS и т.п) приложения.
Если вам приходилось работать над сравнительно большим приложением в тесном контакте с заказчиком, то вы знаете, какой процент требований заказчик выставляет "вдогонку", после утверждения ТЗ. Можно долго обсуждать, насколько это неприятно и неправильно, но с этим, как правило, приходиться жить. Большинство таких "дополнительных" требований касается того, что заказчик видит, т.е. интерфейса: "Тут сделайте жирным, а вот это подвиньте...". Здорово было бы, чтобы эту малоприятную работу делали не суровые java(objective-C)-программисты, а обычные html-css верстальщики, которых, кстати, нанять легче и дешевле. Таким образом приложение с помощью PhoneGap можно сделать не только кроссплатформенным, но и "двухслойным". Интерфейс и навигацию делаем на HTML5, а "тяжёлую" логику, если она есть, делаем нативной. При этом основной разработчик освобождается от вёрстки xml layout-ов, что само по себе уже даёт существенный прирост его производительности.
На выходе получаем вполне здоровый производственный процесс: html-css-javacsript-программисты пишут приложение на PhoneGap, а за всякой нетривиальной задачей обращаются к плагинам, которые пишут "настоящие" Android-разработчики. Вот давайте и посмотрим, насколько легко можно писать и использовать плагины для PhoneGap.

Что бы нам этакого сделать?

Сделаем приложение, которое будет фотографировать, получать данные о текущем местоположении и времени и всё это отправлять куда-нибудь как письмо с вложением, используя стандартное почтовое приложение, установленное на нашем Android-устройстве. Всё, кроме отправки почты мы сделаем стандартными средствами PhoneGap, а для вызова почтового клиента реализуем и используем свой плагин. Создание проекта и привязывание к нему PhoneGap я описал ранее, поэтому сейчас остановимся на собственно программировании.

Главная и единственная страница

Вот код нашей HTML+JavaScript страницы, в которой всё и происходит:

<!DOCTYPE html>
<html>
<head>
    <title>Event Wave</title>
    <script type="text/javascript" src="cordova-2.0.0.js" />
    <script type="text/javascript" src="mailsender.js" />
    <script type="text/javascript">
 
        var imgPath = new Array();
        var coordsLat = "";
        var coordsLon = "";
        var coordsTime = "";
 
        var startPhoto = function() {
            navigator.device.capture.captureImage(captureSuccess, captureError, {limit:1});
        }
 
        var captureSuccess = function(mediaFiles) {
            var i, len;
            for (i = 0, len = mediaFiles.length; i < len; i += 1) {
                imgPath[i] = mediaFiles[i].fullPath;
            }
            navigator.geolocation.getCurrentPosition(onGpsSuccess, onGpsError);
        };
 
        var onGpsSuccess = function(position) {
            coordsLat = position.coords.latitude;
            coordsLon = position.coords.longitude;
            coordsTime = position.timestamp;
 
            MailSender.sendMail("your@email.com",
                    "test subject",
                    "Geo:"+coordsLat+","+coordsLon+" time:"+coordsTime,
                    imgPath[0],
                    function() {
                        navigator.notification.alert('Sended successfully', null, 'MailSender');
                    },
                    function() {
                        navigator.notification.alert('Failed to send email', null, 'MailSender');
                    });
        };
 
        var onGpsError = function(error) {
            navigator.notification.alert('Error: ' + error.code + ' message: ' + error.message, null, 'GPS Error');
        }
 
        var captureError = function(error) {
            navigator.notification.alert('Error: ' + error.code, null, 'Capture Error');
        };
 
 
    </script>
 
</head>
<body style="width: 100%; height: 100%; margin: 0 0 0 0;">
    <div style="margin: 25% 25% 25% 25%; width: 50%; height: 50%; text-align: center;">
        <button onclick="startPhoto()">Send photo</button>
    </div>
 
</body>
</html>

Тут мы последовательно вызываем:

1. Окно фотографирования
При вызове метода captureImage мы указываем callback-функции на случай успеха и ошибки, а также параметры (в данном случае говорим, что ждём только одну фотографию).

2. Метод поучения геокоординат
Вызываем в onSuccess фотографирования. Получаем координаты и отметку времени. Как и путь к фотографии укладываем полученные данные в глобальные переменные.

3. Метод нашего плагина
В этот метод передаём всё что собрали, а он уже делает то, что нам нужно.

Плагин

Как видно из схемы в начале поста, плагин устроен так же как и сам PhoneGap, т.е. состоит из js и java-части. При чём обе части реализуются на удивление просто.
mailsender.js:

var MailSender = new Array();
 
MailSender.sendMail = function(mail, subject, text, attachment, success, fail) {
 
    var params = {mail:mail, subject:subject, text:text, attachment:attachment};
    return cordova.exec(function(args) {
        success(args);
    }, function(args) {
        fail(args);
    }, 'MailSender', 'send', [params]);
}

Тут просто собираем все данные в объект и отдаём методу exec, который используется в PhoneGap для передачи управления нативному коду плагина. Параметрами в этот метод передаём также callback-функции, имя плагина в реестре и action - имя идентификатора действия.
Реестр плагинов - это просто файл res/xml/config.xml, куда нужно добавить строку вида: 

<plugin name="MailSender" value="your.plugin.package.name.MailSender"/>

И, наконец, MailSender.java:

import android.content.Intent;
import android.net.Uri;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONObject;
 
public class MailSender extends Plugin {
 
    public static final String SEND = "send";
 
    @Override
    public PluginResult execute(String action, JSONArray args, String callbackId) {
        PluginResult rez = new PluginResult(com.phonegap.api.PluginResult.Status.INVALID_ACTION);
        try {
            if (SEND.equals(action) && args.length()==1) {
                JSONObject jo = args.getJSONObject(0);
                Intent it = new Intent(Intent.ACTION_SEND);
                it.putExtra(Intent.EXTRA_EMAIL, jo.optString("email"));
                it.putExtra(Intent.EXTRA_TEXT, jo.optString("text"));
                it.putExtra(Intent.EXTRA_SUBJECT, jo.optString("subject"));
                it.putExtra(Intent.EXTRA_STREAM, Uri.parse(jo.optString("attachment")));
                it.setType("plain/text");
                this.cordova.getActivity().startActivity(Intent.createChooser(it, "Choose Email Client"));
                rez = new PluginResult(PluginResult.Status.OK);
            }
        } catch (Exception e) {
            rez = new PluginResult(PluginResult.Status.ERROR);
            e.printStackTrace();
        }
        return rez;
    }
}

Тут становится понятно, зачем нужен action: один метод exec можно использовать для создания нескольких "фасадных" javascript-методов, представляющих разные нативные действия.


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

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