Этот пост участвует в конкурсе "Умные телефоны за умные посты".
Сейчас немало мобильных приложений пишутся с использованием HTML и Javascript. Оно и понятно — подобные приложения легче писать, легче переносить с одной мобильной платформы на другую, не нужно осваивать Java, Objective C и другие языки. Однако для большинства мобильных ОС все-таки требуется некая обертка. В самом простом случае необходимо написать небольшое приложение, которое будет представлять собой развернутое на максимум окошко встроенного веб-браузера. Для поддержки специальных возможностей (например, работы с контактами или файлами) нужно будет либо дописывать его для поддержки нужных функций, либо воспользоваться одним из фреймворков для написания мобильных приложений. В любом случае, вам понадобятся специальные инструменты, будь то компилятор или тот же фреймворк.
Однако в Symbian, начиная с Symbian S60 3rd Edition, появилась одна хорошая штука — Symbian Web Runtime (WRT). Она позволяет разрабатывать свои мобильные приложения с использованием HTML и Javascript, используя только стандартные средства практически любой десктопной операционки — текстовый редактор и ZIP-архиватор. По сути, это тоже мобильный фреймворк, но уже встроенный в систему, не требующий дополнительных инструментов или компиляции. Давайте посмотрим на него поближе?
Hello, world!
Будущее WRT-приложение (или правильнее, WRT-виджет) при первом рассмотрении представляет собой ZIP-архив с расширением .WGZ. В нем находится папка с файлами (если просто заархивировать файлы — работать не будет, только в папке). Внутри обязательно должен быть файл info.plist и основной HTML-файл. Также может быть файл icon.png с рекомендуемым размером 88x88 пикселей, который будет служить иконкой нашего виджета. Все остальное — на ваше усмотрение.
Формат файла info.plist почти точно такой же, как у Apple, и описан здесь. Вот пример:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Nokia//DTD PLIST 1.0//EN" "http://www.nokia.com/NOKIA_COM_1/DTDs/plist-1.0.dtd">
<plist version="1.0">
<dict>
<key>DisplayName</key>
<string>HelloWorld</string>
<key>Identifier</key>
<string>com.HelloWorld</string>
<key>Version</key>
<string>1.0</string>
<key>MainHTML</key>
<string>index.html</string>
<key>MiniViewEnabled</key>
<false/>
<key>AllowNetworkAccess</key>
<false/>
</dict>
</plist>
Параметр | Тип | Использование | Описание |
DisplayName | Строка | Обязательный | Название вашего виджета. Будет отображаться в главном меню телефона. |
Identifier | Строка | Обязательный | Уникальный идентификатор виджета. |
MainHTML | Строка | Обязательный | Относительный путь к основному HTML-файлу. |
Version | Строка | Необязательный | Версия виджета. |
AllowNetworkAccess | Boolean | Необязательный | Дать виджету доступ к сети. По умолчанию false — доступ только к локальным данным. |
MiniViewEnabled | Boolean | Необязательный | Дать возможность добавлять виджет на рабочий стол. По умолчанию false. Если устройство не поддерживает виджеты рабочего стола, то этот параметр игнорируется. |
Если теперь создать файл index.html в той же папке, в нем написать что-то типа этого:
<html>
<body>
<h1>Hello, world!</h1>
</body>
</html>
А затем сжать эту папку в ZIP-архив и поменять у него разрешение на .WGZ, то мы уже получим приложение для Symbian.
Вот для начала и все. Можете верстать странички, писать код на Javascript для них, тестировать в своем браузере, а потом легко и просто превращать их в мобильные приложения для Symbian. Стоит однако учесть, что для подключения CSS и Javascript файлов из интернета вам нужно будет включить параметр AllowNetworkAccess.
Локализация
Если вы желаете перевести свой виджет на множество языков, вам помогут встроенные средства локализации. Это довольно просто.
Для поддержки любого языка вам нужно создать внутри основной папки подпапку с названием <язык>.lproj (например, "en.lproj" или "fi.lproj"):
Если вы желаете перевести название вашего виджета, вам нужно создать файл infoplist.strings и поместить в него строчку примерно такого вида:
DisplayName = "Локализованное название"
В остальном процесс локализации довольно прост. При загрузке файлов с каскадными таблицами стилей, скриптами, картинками или другими ресурсами WRT ищет их сначала в подпапке с локализацией, и только потом — в основной папке. Например:
[основная папка]\fi.lproj\flag.png
[основная папка]\en.lproj\flag.png
[основная папка]\flag.png
Javascript
var flag = document.createElement('img');
flag.setAttribute('src', 'flag.png');
В результате для финского языка выведет финский флаг, для английского — английский, а для всех остальных — тот фиолетовый.
Web Runtime API
Отдельно стоит упомянуть об WRT API. Оно позволяет осуществлять взаимодействие с Symbian. В WRT 1.0 функций не особо много, но начиная с WRT 1.1 есть возможность работать со многими сервисами этой мобильной операционки. Об работе с сервисами — чуть ниже. Полную документацию вы можете найти на странице «Symbian Web Runtime API reference» официальной документации. Ниже я вкратце переведу часть этой документации. Некоторые моменты в документации просто не описаны или спорны, так что если заметите ошибку — пожалуйста, напишите о ней, и я ее исправлю.
Существует несколько специальных объектов WRT, к которым можно обращаться из Javascript:
- widget — базовые возможности WRT;
- menu — работа с меню приложения;
- MenuItem — класс для создания пункта меню;
- device — работа с сервисами Symbian; появилось в WRT 1.1.
Кроме того, поддерживается объект screen, позволяющий получить параметры экрана устройства.
Давайте рассмотрим их по очереди.
widget
Доступен как window.widget или просто widget. При этом слово widget является в WRT зарезервированным и не должно использоваться как название переменной или функции.
Список методов объекта widget:
- openURL(String url) — открытие адреса url во встроенном браузере. При этом виджет не закрывается, а остается работать в фоне. Адрес url должен подчиняться RFC 1738 и все не-ASCII символы (например, русские буквы) должны быть предварительно закодированы.
- setDisplayLandscape() — изменение ориентации на ландшафтную, если это поддерживается устройством.
- setDisplayPortrait() — изменение ориентации на портретную, если это поддерживается устройством.
- setPreferenceForKey(String preference, String key) — сохранение пары ключ-значение во встроенном хранилище. key — ключ, preference — значение. Для удаления пары из хранилища нужно передать в качестве значения null.
- preferenceForKey(String key) — получение значения пары ключ-значение из встроенного хранилища. key — ключ. Если пара отсутствует, возвращает undefined.
- prepareForTransition(String transitionMode) — подготовка к анимации. transitionMode — вид анимации, пока что поддерживает только "fade".
- performTransition() — выполнение анимации.
- setNavigationEnabled(Boolean navigationMode) — установка режима навигации между элементами. Если navigationMode установлен в true, навигация осуществляется в помощью курсора. Если в false — с помощью перемещения между элементами. По умолчанию true — с помощью курсора.
- setNavigationEnabled(String navigationMode) — установка режима навигации между элементами. Если navigationMode установлен в "none", навигация осуществляется в помощью курсора. Если в "tabbed" — с помощью перемещения между элементами. По умолчанию "none" — с помощью курсора.
- openApplication(HexNumber Uid, String param) — открытие приложения с кодом Uid и параметрами param. Список кодов приложений:
- 0x10008D39 — веб-браузер
- 0x100058C5 — сообщения
- 0x101f4cce — контакты
- 0x101f4cd5 — лог (список последних звонков и SMS)
- 0x100058F8 — профили
- 0x10005901 — календарь
- 0x10005903 — часы
- 0x100058CA — звукозапись
- 0x101F4668 — конвертер единиц
- 0x10005902 — калькулятор
- 0x1000599d — заметки
- 0x101f84eb — менеджер файлов
- 0x101f8599 — галерея
- 0x101f857a — камера (на устройстве с одной камерой)
- 0x101ffa86 — камера (на устройстве с двумя камерами)
- 0x102072c3 — проигрыватель
- 0x10005a3e — RealPlayer
- 0x10005951 — Bluetooth
- 0x1000594d — инфракрасник
- 0x100058ec — настройки
- 0x10005a32 — темы оформления
Поясню, в чем суть функций prepareForTransition и performTransition. Допустим, вы желаете скрыть или показать какой-то элемент. Для этого вы используете elem.style.display = "block" или elem.style.display = "none". Однако если вы желаете анимировать этот показ или скрытие, вы должны поставить перед ним вызов widget.prepareForTransition("fade"), а после него — widget.performTransition().
Вот пример из документации:
HTML
<!-- Основной блок -->
<div id='main'>
Привет, мир!
<input type="button" value="Config" onclick="toMain(0);" />
</div>
<!-- Конец основного блока -->
<!-- Блок настроек -->
<div id='config'>
Твой мир теперь мой, бугогашеньки!
<input type="button" value="Main" onclick="toMain(1);" />
</div>
<!-- Конец блока настроек -->
Javascript
function toMain(main) {
// подготовка к анимации
widget.prepareForTransition("fade");
if (main) { // переход от блока настроек к основному блоку
// скрытие блока настроек
document.getElementById("config").style.display = 'none';
// показ основного блока
document.getElementById("main").style.display = 'block';
}
else { // переход от основного блока к блоку настроек
// скрытие основного блока
document.getElementById("main").style.display = 'none';
// показ блока настроек
document.getElementById("config").style.display = 'block';
}
// анимация
widget.performTransition();
}
Список свойств объекта widget:
- identifier — идентификатор виджета, объявленный в файле info.plist.
- onshow — обработчик события показа виджета. Вызывается при переходе виджета из фона в активное состояние.
- onhide — обработчик события скрытия виджета. Вызывается при переходе виджета в фоновое состояние.
- onexit — обработчик события закрытия виджета. Вызывается при закрытии виджета.
- isrotationsupported — если устройство поддерживает переключение между портретным и ландшафтным режимами, это свойство установлено в true. Если устройство поддерживает только один режим, то в false. Необходимо проверять значение этого свойства перед вызовами widget.setDisplayLandscape() и widget.setDisplayPortrait().
Подобъекты объекта widget:
- wrt — содержит информацию об устройстве и версии WRT:
- version — версия WRT. На устройствах, использующих Browser/WRT 7.0, будет содержать "1.1". На устройствах с Browser/WRT 7.1 будет содержать версию браузера (например, "BrowserNG/7.1.13841").
- platform.id — код ОС (например, "S60").
- platform.romVersion — версия прошивки (например, "v 31.0.008 24-8-2009 RM-365 © NMP").
- platform.manufacturer — название производителя (например, "Nokia").
- platform.packageVersion — версия browser/WRT. На устройствах с browser/WRT будет "7.00(0)", с browser/WRT 7.1 — "7.01(0)".
- platform.model — модель устройства (например, "N97").
menu
Доступен как window.menu или просто menu. При этом слово menu является в WRT зарезервированным и не должно использоваться как название переменной или функции.
При помощи этого объекта можно управлять меню приложения. Меню закреплено за левой программной кнопкой и не может быть переназначено на любую другую (например, на правую). Левая кнопка называется "Options" (или иначе в разных локализациях). Правая программная кнопка назвается "Exit" и завершает приложение. По умолчанию программные кнопки скрыты и показываются лишь тогда, когда пользователь нажимает одну из них (на клавиатуре устройства).
Список методов объекта menu:
- append(MenuItem menuItem) — добавление в меню пункта menuItem.
- remove(MenuItem menuItem) — удаление из меню пункта menuItem.
- getMenuItemById(Integer id) — получение объекта MenuItem с кодом id. Если такой отсутствует, возвращается undefined.
- getMenuItemByName(String menuItemLabel) — получение объекта MenuItem с названием menuItemLabel. Если такой отсутствует, возвращается undefined. Что будет, если у двух пунктов совпадают названия, в документации не описано, но скорее всего вернет первый найденный.
- setLeftSoftkeyLabel(String label, Function callbackfunc) — установка названия левой программной кнопки. Если title — пустая строка, то будет установлено значение по умолчанию ("Options" или др. в зависимости от локализации). callbackfunc — функция, вызываемая при нажатии на кнопку (по умолчанию — показ меню). Если в callbackfunc передано null, возвращает кнопке функцию по умолчанию.
- setRightSoftkeyLabel(String label, Function callbackfunc) — установка названия правой программной кнопки. Если title — пустая строка, то будет установлено значение по умолчанию ("Exit" или др. в зависимости от локализации). callbackfunc — функция, вызываемая при нажатии на кнопку (по умолчанию — завершение приложения). Если в callbackfunc передано null, возвращает кнопке функцию по умолчанию.
- showSoftkeys() — показать программные кнопки.
- hideSoftkeys() — скрыть программные кнопки.
- clear() — очистить меню.
Список свойств объекта menu:
- onShow — обработчик события показа меню. Вызывается при показе меню.
MenuItem
Пункт меню.
Список методов объекта MenuItem:
- new MenuItem(String label, Integer id) — конструктор. Создает новый пункт экземпляр MenuItem с названием label и кодом id.
- append(MenuItem childMenuItem) — добавление в подменю пункта childMenuItem.
- remove(MenuItem childMenuItem) — удаление из подменю пункта childMenuItem.
- setDimmed(Boolean flag) — устанавливает, показывать или нет пункт меню. Если false, то пункт меню не показывается. Если true — пункт меню будет показан. По умолчанию true — показывать.
Список свойств объекта MenuItem:
- onSelect — обработчик события выбора пункта меню. Вызывается при выборе пункта меню. В функцию-обработчик также может передаваться Integer id — код выбранного пункта меню.
Пример из документации:
// Обработчик меню
function menuEventHandler(id)
{
switch (id)
{
case 2001:
break;
case 2002:
// что-то сделать
break;
}
}
// функция создания меню
function createMenu()
{
// получение объекта меню
var optionsMenu = window.menu;
// установка функции-обработчика показа меню
optionsMenu.onShow = function()
{
// вывод сообщения
alert('Event Trigger: optionsMenu.onShow');
}
// создание двух пунктов меню
var m1 = new MenuItem('Песец', 2001);
var m2 = new MenuItem('Бобер', 2002);
// установка функции-обработчика выбора пунктов меню
m1.onSelect = menuEventHandler;
m2.onSelect = menuEventHandler;
// добавление этих двух пунктов в меню
optionsMenu.append(m1);
optionsMenu.append(m2);
// создание еще двух пунктов меню для организации подменю
var m11 = new MenuItem('Северный', 3001);
var m12 = new MenuItem('Полный', 3002);
// добавление этих двух пунктов в подменю к пункту "Песец"
// с поиском пункта меню по его id
optionsMenu.getMenuItemById(2001).append(m11);
// с поиском пункта меню по его названию
optionsMenu.getMenuItemByName('Песец').append(m12);
// Установка функции-обработчика выбора пунктов меню
m11.onSelect = menuEventHandler;
m12.onSelect = menuEventHandler;
}
device
Доступен как window.device или просто device. При этом слово device является в WRT зарезервированным и не должно использоваться как название переменной или функции.
Этот объект присутствует начиная с WRT 1.1 и открывает огромные возможности по работе с сервисами Symbian при помощи всего одной функции.
Список методов объекта device:
- getServiceObject(String provider, String interface) — получение объекта сервиса от провайдера provider и с интерфейсом interface.
Сервис Провайдер Интерфейс AppManager (сервис приложений) Service.AppManager IAppManager Calendar (сервис календаря) Service.Calendar IDataSource Contact (сервис контактов) Service.Contact IDataSource Landmarks (сервис точек пути) Service.Landmarks IDataSource Location (сервис геолокации) Service.Location ILocation Logging (сервис журналов) Service.Logging IDataSource Media Management (сервис управления медиа) Service.MediaManagement IDataSource Messaging (сервис сообщений) Service.Messaging IMessaging Sensors (сервис сенсоров) Service.Sensor ISensor System Information (сервис системной информации) Service.SysInfo ISysInfo
Если сервис недоступен на устройстве, то метод вернет undefined и вызовет исключение. При первой попытке доступа к сервису пользователю будет выведено диалоговое окно с вопросом о предоставлении прав на использование сервиса («Приложение желает получить доступ к… Разрешить?»).
- getServicePermissions(Array serviceArray) — запрос у пользователя разрешения для работы с сервисами. Список сервисов и интерфейсов перечивляется в формате {"провайдер": "интерфейс", "провайдер": "интерфейс",… }. Используется для получения разрешения на использование сервисов от пользователя в одном диалоговом окне. Использовать необязательно, но желательно. Не возвращает значений, но вызывает исключения при ошибке.
Сам по себе раздел об использовании сервисов Symbian в WRT довольно большой, и приводить его здесь я не буду. Если вы сочтете это интересным, я могу описать его в отдельной статье. Вообще, документация более-менее понятна и для каждого сервиса есть развернутый пример по всем функциям. Кстати, большинство функций имеют синхронный и асинхронный варианты, так что ваше приложение тормозить не будет.
Плюсы и минусы
Плюсы:
- легкость разработки;
- возможность разработки без устройства под рукой (в эмуляторе или веб-браузере);
- возможность использования множества сервисов Symbian;
- наличие документации и примеров;
- к сожалению, на большинстве устройств используется довольно старая версия WebKit, которая не поддерживает HTML 5 и CSS 3;
- также на части устройств до сих пор используется WRT 1.0, функциональность которого крайне мала;
- документация местами путана и противоречива, нет русского перевода.
Кстати говоря, на тех же Nokia N9 и Nokia N950, на базе ОС MeeGo с браузером Nokia Browser 8.5, HTML 5 вполне поддерживается — вы можете, к примеру, написать игру с использованием Canvas, с поддержкой жестов и акселерометра. Правда, там уже используется не WRT, а возможности HTML 5.
Спасибо за прочтение. Извиняюсь за возможные ошибки и буду рад полезным замечаниям.