Мобильные приложения созданные с помощью веб-технологий понемногу захватывают мир. Но создание таких приложений, под популярные платформы, связанны с кучей проблем — от неизвестных истории багов, зоопарка размеров экрана, до проблем с производительностью, которые не решаются просто переписыванием тонких мест.
Но к счастью, этот топик не будет наполнен обыденной трагедией разработки, подобных приложений. Поскольку сегодня, я покажу на реальном примере, как разрабатывать приложения под Firefox OS, которая поддерживает большую часть современных веб-технологий, и вообще говоря создана для них и благодаря им.
Для приготовления нам понадобиться:
0) Апи какого-нибудь сервиса, в нашем случае это ФотоФания (не сочтите за рекламу).
1) Angular js — в качестве основы
2) buildingfirefoxos.com/building-blocks — заготовки ui-блоков
3) jQuery и прочие либы по вкусу
Поскольку хочется показать реальное приложение, то и апи надо использовать реальное. ФотоФания это сайт с помощью которого из своих скучных сэлфи можно создать веселые фоточки. Так что нам, немного, придется работать с изображениями на клиенте (хотя основная работа будет происходить на сервере, разумеется).
Angular хорош. Конечно у меня есть притенении к нему, с точки зрения производительности на мобильных девайсах, но он позволяет отстранится от рутинного кода, обновлений элементов и прочего, занимаясь только данными. Кроме того, приложение на Angular без особых усилий получается отлично структурированным.
Building Blocks — это готовые элементы дизайна, взятые (частично) из исходников Gaia — UI прослойки Firefox OS. Эти заготовки не используют js, вся логика, которая там есть, реализована через псевдо-селекторы. При этом, весь код написан с помощью html5 элементов, и использует data-* атрибуты для обозначения роли блока.
Еще я заметил важную вещь — код Building Blocks работает быстрее и плавнее чем код, который я написал сам, как бы я не старался. По крайней мере, это касается списков. Возможно определённые элементы ускоряются нативно — не уверен насколько это правда. Так что по возможности пользуйтесь заготовками — они ускоряют разработку, делают приложение похожим на нативное и делают его плавнее.
Вообще говоря, я бы с радостью избавился от jQuery, но он является зависимостью плагина для пинч-зума github.com/segdeha/jquery-pan-zoom. Подсадить плагин на Zepto не получилось. К счастью jQuery можно билдить из тех кусков которые вам нужны. Поэтому я беспощадно избавился от SIzzle и прочих ненужных в быту вещей.
Структуру приложения каждый использует какую хочет. В моем случае она похожа на
Скрипты и стили билдятся с помощью grunt в реальном времени. А grunt release уже все минифицирует. Так же я использую grunt-angular-templates который все шаблоны превращает в js код, который так же добавляется в build.js
Думаю некоторые заметили ng-csp в элементе. Эта директива включает поддержку Content Security Policy — и это является обязательным, для создания приложения для Firefox OS. Кроме того, нам надо немного пропатчить сам angular!
Ищем в коде angular строчку
А так же нам надо добавить руками в наши стили — вспомогательные стили angular.js. А именно — code.angularjs.org/1.2.16/angular-csp.css
Надо заметить, что я изначально пишу код на coffeescript, но буду вам показывать его javascript аналог. Возможно где-то всплывут неточности — тогда мы вместе их исправим.
Еще я надеюсь, что вы знакомы с angular и js достаточно хорошо, чтобы мне не останавливаться на каждом шагу. Иначе эту статью никто никогда в жизни не дочитает.
Наше приложение будет мультиязычным. Поэтому нам требуется словарь, его интерпретатор и какая-нибудь директива или фильтр, чтобы вставлять слова в код. Часто когда речь заходит об мультиязычности с помощью angular — всплывают какие-то фильтры, которые выглядят не очень приятно. Например нам предлагают писать так меня это немного корежит — зачем вызывать каждый раз лишнюю функцию и писать такой неудобный код, если можно просто использовать словарь в глобал-скоупе, и обращаться к нему? Например так . За всё время моей работы, такой способ меня не подводил. Давайте же его и реализуем.
Хочется чтобы он был любой вложенностью, поддерживал любое количество языков. Без проблем!
Структура есть, теперь, чтобы не обращаться напрямую переменной (а не языку), надо написать небольшой парсер.
То есть мы просто рекурсивно доходим, до того момента, когда значение переменной становится не объектом, и возвращаем значение с нужным языком. Все просто, и теперь у нас есть переменная m в рут-скоупе, к которой мы можем смело обращаться.
Кто-то наверняка засомневается в производительности такого решения, но могу вас уверить, что это капля в море. И вообще вам никто не мешает написать директиву, которая не будет вешать ватчер на переменную. Так что все в порядке.
Давайте доделаем drawer (Меню). Допустим у нас есть уже основа в виде js. Есть контроллер который обслуживает наше меню, и мы уже получили с помощью апи список категорий, которые будем выводить в меню. Тогда код меню получается очень простым.
Как видно, в своей разметке index.html мы уже использовали часть кода из Building Blocks. Сейчас мы возьмем код Drawer'a и немного изменим под себя. buildingfirefoxos.com/building-blocks/drawer.html
Как видно у нас два списка в меню, в самом верхнем лежит только одна ссылка на избранное (заметили что мы уже используем нашу переменную m?), там же выводится кол-во избранных фотоэффектов, сбоку. Ниже у нас, собственно, сам список категорий.
И для полноты картины сделаем еще одну страницу — список эффектов, то есть страница категории.
buildingfirefoxos.com/building-blocks/headers.html — тут берем хедеры
buildingfirefoxos.com/building-blocks/lists.html — тут списки
buildingfirefoxos.com/building-blocks/filters.html — тут фильтры (табы)
buildingfirefoxos.com/building-blocks/buttons.html — а тут кнопочи
И получаем, упрощенно, что-то такое:
Надеюсь все понятно. А понять надо следущее — мы берем готовые блоки и вырезаем из них самое нужное нам. Ими можно крутить и вертеть как угодно.
Таким же образом делаются все остальные страницы.
В приложениях Firefox OS версии >= 1.3 input[file] работать не будет, в замен его, нам предлагают пользоваться MozActivity. Что даже упрощает работу.
Вот так выглядит получение картинки:
Вот так шеринг:
И примерно так — сохранение:
После того, как приложение готово, его надо сбилдить и протестировать.
Чтобы билдить — можно использовать эмуляторы.
Я вам предлагаю использовать Менеджер приложений, который встроен по умолчанию в новые версии firefox. developer.mozilla.org/ru/docs/Mozilla/Firefox_OS/Using_the_App_Manager. Тут же можно устанавливать разные версии эмуляторов, подключать девайсы и дебажить! Что очень удобно.
Так же нам нужен манифест приложения. Выглядит и пишется он очень просто.
Приведу пример:
Собственно говоря, засовываем все нужные файлы в .zip архив и отправляем в marketplace.firefox.com на проверку. Через пару дней (а может и к вечеру) приложение появится в маркете!
Я хотел донести до вас, что разработка под Firefox OS это очень просто, интересно и весело! Конечно, пока что, на этом денег не заработать. Но можно подумать, мы здесь ради денег собрались. Так что, берите IDE в руки, и создавайте приложения под Firefox OS!
Тем,лучшим из нас, у кого есть девайс на Firefox OS — прошу к столу marketplace.firefox.com/app/photofunia
Все пожелания, а так же ошибки и недочеты принимаю в личку.
Но к счастью, этот топик не будет наполнен обыденной трагедией разработки, подобных приложений. Поскольку сегодня, я покажу на реальном примере, как разрабатывать приложения под Firefox OS, которая поддерживает большую часть современных веб-технологий, и вообще говоря создана для них и благодаря им.
Рецепт
Для приготовления нам понадобиться:
0) Апи какого-нибудь сервиса, в нашем случае это ФотоФания (не сочтите за рекламу).
1) Angular js — в качестве основы
2) buildingfirefoxos.com/building-blocks — заготовки ui-блоков
3) jQuery и прочие либы по вкусу
0) Апи
Поскольку хочется показать реальное приложение, то и апи надо использовать реальное. ФотоФания это сайт с помощью которого из своих скучных сэлфи можно создать веселые фоточки. Так что нам, немного, придется работать с изображениями на клиенте (хотя основная работа будет происходить на сервере, разумеется).
1) Angular js
Angular хорош. Конечно у меня есть притенении к нему, с точки зрения производительности на мобильных девайсах, но он позволяет отстранится от рутинного кода, обновлений элементов и прочего, занимаясь только данными. Кроме того, приложение на Angular без особых усилий получается отлично структурированным.
2) Building Blocks
Building Blocks — это готовые элементы дизайна, взятые (частично) из исходников Gaia — UI прослойки Firefox OS. Эти заготовки не используют js, вся логика, которая там есть, реализована через псевдо-селекторы. При этом, весь код написан с помощью html5 элементов, и использует data-* атрибуты для обозначения роли блока.
Еще я заметил важную вещь — код Building Blocks работает быстрее и плавнее чем код, который я написал сам, как бы я не старался. По крайней мере, это касается списков. Возможно определённые элементы ускоряются нативно — не уверен насколько это правда. Так что по возможности пользуйтесь заготовками — они ускоряют разработку, делают приложение похожим на нативное и делают его плавнее.
3) jQuery и прочие
Вообще говоря, я бы с радостью избавился от jQuery, но он является зависимостью плагина для пинч-зума github.com/segdeha/jquery-pan-zoom. Подсадить плагин на Zepto не получилось. К счастью jQuery можно билдить из тех кусков которые вам нужны. Поэтому я беспощадно избавился от SIzzle и прочих ненужных в быту вещей.
Немного о структуре
Структуру приложения каждый использует какую хочет. В моем случае она похожа на
/build/
/build.js
/build.css
/scripts/
/vendors/
/controllers/
/services/
/.../
/app.js
/dictionary.js
/config.js
/styles/
/helpers/
/variables.less
/mixins.less
/main.less
/header.less
/...
/images/
index.html
Скрипты и стили билдятся с помощью grunt в реальном времени. А grunt release уже все минифицирует. Так же я использую grunt-angular-templates который все шаблоны превращает в js код, который так же добавляется в build.js
Первоначальная разметка страницы
<!doctype html>
<html ng-app="PhotoFunia" ng-csp>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta charset="UTF-8">
<title>PhotoFunia</title>
<link rel="stylesheet" href="build/build.css"/>
</head>
<body role="application">
<!-- Прелоадер, который показывается при старте. Я не выношу его в отдельный файл, в надежде что так будет быстрее-->
<section ng-controller="PreloaderController">
<div id="preloader" ng-hide="preloaderHide">
<div class="preloader-content">
<div class="logo"></div>
</div>
</div>
</section>
<!-- Это наш дроуэр (меню) которое будет выдвигаться слева -->
<section data-type="sidebar"
ng-include="VIEWS.DRAWER"></section>
<!-- Это наш контент. Не знаю почему у него id="drower" так нам сказал building blocks -->
<section id="drawer" role="region" ng-view
ng-class="{'menu-opened': drawerOpened}"></section>
<!-- Это своеобраный toast для FF OS. http://buildingfirefoxos.com/building-blocks/status.html -->
<section role="status" ng-controller="ToastController">
<p ng-if="text" ng-bind="text"></p>
</section>
<!-- Тут скрывается универсальный код для попапов -->
<section ng-include="VIEWS.POPUP"></section>
<script src="build/build.js"></script>
</body>
</html>
Важно
Думаю некоторые заметили ng-csp в элементе. Эта директива включает поддержку Content Security Policy — и это является обязательным, для создания приложения для Firefox OS. Кроме того, нам надо немного пропатчить сам angular!
Ищем в коде angular строчку
window.XMLHttpRequest();
и заменяем на window.XMLHttpRequest({mozSystem: true});
А так же нам надо добавить руками в наши стили — вспомогательные стили angular.js. А именно — code.angularjs.org/1.2.16/angular-csp.css
Немного о js
Надо заметить, что я изначально пишу код на coffeescript, но буду вам показывать его javascript аналог. Возможно где-то всплывут неточности — тогда мы вместе их исправим.
Еще я надеюсь, что вы знакомы с angular и js достаточно хорошо, чтобы мне не останавливаться на каждом шагу. Иначе эту статью никто никогда в жизни не дочитает.
Мультиязычность
Наше приложение будет мультиязычным. Поэтому нам требуется словарь, его интерпретатор и какая-нибудь директива или фильтр, чтобы вставлять слова в код. Часто когда речь заходит об мультиязычности с помощью angular — всплывают какие-то фильтры, которые выглядят не очень приятно. Например нам предлагают писать так меня это немного корежит — зачем вызывать каждый раз лишнюю функцию и писать такой неудобный код, если можно просто использовать словарь в глобал-скоупе, и обращаться к нему? Например так . За всё время моей работы, такой способ меня не подводил. Давайте же его и реализуем.
Хочется чтобы он был любой вложенностью, поддерживал любое количество языков. Без проблем!
var DICTIONARY = {
header: {
search: {
title: {
ru: 'Поиск',
en: 'Search'
},
favorites: {
ru: 'Избранное',
en: 'Favorites'
}
}
},
cancel: {
ru: 'Отмена',
en: 'Cancel'
}
};
Структура есть, теперь, чтобы не обращаться напрямую переменной (а не языку), надо написать небольшой парсер.
App.run(['$rootScope', function ($rootScope) {
var lang = 'ru'; // тут должна быть какая-то логика :)
$rootScope.m = (function() {
var parse = function(obj, result) {
result = result || {};
for (var key in obj) {
var value = obj[key];
if (typeof value !== "object")
return obj[lang];
result[key] = parse(value);
}
return result;
};
return parse(DICTIONARY);
})();
}]);
То есть мы просто рекурсивно доходим, до того момента, когда значение переменной становится не объектом, и возвращаем значение с нужным языком. Все просто, и теперь у нас есть переменная m в рут-скоупе, к которой мы можем смело обращаться.
Кто-то наверняка засомневается в производительности такого решения, но могу вас уверить, что это капля в море. И вообще вам никто не мешает написать директиву, которая не будет вешать ватчер на переменную. Так что все в порядке.
Вернемся к разметке
Давайте доделаем drawer (Меню). Допустим у нас есть уже основа в виде js. Есть контроллер который обслуживает наше меню, и мы уже получили с помощью апи список категорий, которые будем выводить в меню. Тогда код меню получается очень простым.
Как видно, в своей разметке index.html мы уже использовали часть кода из Building Blocks. Сейчас мы возьмем код Drawer'a и немного изменим под себя. buildingfirefoxos.com/building-blocks/drawer.html
<nav ng-controller="DrawerController">
<div class="empty-space"></div>
<ul>
<li>
<a ng-click="go('/favorites'); closeDrawer()">
<span class="text" ng-bind="m.menu.favorite"></span>
<span class="counter" ng-bind="favorite.get().length"></span>
</a>
</li>
</ul>
<h2 ng-bind="m.category.title"></h2>
<ul>
<li ng-repeat="cat in categories">
<a ng-click="openCategory(cat.key)">
<span class="text" ng-bind="cat.title"></span>
<span class="counter" ng-bind="cat.count"></span>
<span class="new-counter" ng-if="cat.new_count" ng-bind="'+'+cat.new_count"></span>
</a>
</li>
</ul>
</nav>
Как видно у нас два списка в меню, в самом верхнем лежит только одна ссылка на избранное (заметили что мы уже используем нашу переменную m?), там же выводится кол-во избранных фотоэффектов, сбоку. Ниже у нас, собственно, сам список категорий.
И для полноты картины сделаем еще одну страницу — список эффектов, то есть страница категории.
buildingfirefoxos.com/building-blocks/headers.html — тут берем хедеры
buildingfirefoxos.com/building-blocks/lists.html — тут списки
buildingfirefoxos.com/building-blocks/filters.html — тут фильтры (табы)
buildingfirefoxos.com/building-blocks/buttons.html — а тут кнопочи
И получаем, упрощенно, что-то такое:
<!-- Наш хедер, собсвенной персоной -->
<header>
<menu type="toolbar">
<!-- Кнопочка для поиска -->
<a ng-click="go('/search')">
<span class="icon action-icon search"></span>
</a>
</menu>
<!-- Две кнопки для открытия / закрытия меню (drawer) -->
<a ng-click="closeDrawer()"><span class="icon icon-menu"></span></a>
<a ng-click="openDrawer()"><span class="icon icon-menu"></span></a>
<h1 ng-bind="category.title"></h1>
</header>
<div role="main" data-type="list" id="category">
<!-- Табы, которые устанавливают сортировку эффектов -->
<ul role="tablist" data-type="filter" data-items="2">
<li role="tab" aria-selected="{{ sorting === 'new'}}">
<a ng-click="setSorting('new')" text="m.category.new"></a>
</li>
<li role="tab" aria-selected="{{ sorting === 'popular'}}">
<a ng-click="setSorting('popular')" text="m.category.popular"></a>
</li>
</ul>
<!-- И наши эффекты собственной персоной -->
<ul class="effects">
<li ng-repeat="effect in effects"
ng-click="openEffect(effect.key)">
<aside class="pack-end">
<img ng-src="{{CONFIG.DOMAIN + effect.icon}}">
</aside>
<a>
<p ng-bind="effect.title"></p>
<p ng-if="effect.labels">
<span ng-repeat="label in effect.labels"
ng-class="label" class="label"></span>
</p>
</a>
</li>
</ul>
<!-- Кнопочка для подгрузки эффектов -->
<div class="load-more" ng-if="isNeedMore && effects.length">
<button ng-click="showMore()" text="m.category.show_more"></button>
</div>
</div>
Надеюсь все понятно. А понять надо следущее — мы берем готовые блоки и вырезаем из них самое нужное нам. Ими можно крутить и вертеть как угодно.
Таким же образом делаются все остальные страницы.
Об API Firefox OS
В приложениях Firefox OS версии >= 1.3 input[file] работать не будет, в замен его, нам предлагают пользоваться MozActivity. Что даже упрощает работу.
Вот так выглядит получение картинки:
var pick = new MozActivity({
name: "pick",
data: {
type: ["image/*"],
nocrop: true
}
});
pick.onsuccess = function() {
var blob = pick.result.blob;
// что-то делаем с блобом
};
Вот так шеринг:
new MozActivity({
name: "share",
data: {
type: "image/*",
number: 1,
blobs: [blob]
}
});
И примерно так — сохранение:
var sdcard = navigator.getDeviceStorage("sdcard");
var request = sdcard.addNamed(blob, name);
request.onsuccess = function() {};
request.onerror = function() {};
После того, как приложение готово, его надо сбилдить и протестировать.
Чтобы билдить — можно использовать эмуляторы.
Я вам предлагаю использовать Менеджер приложений, который встроен по умолчанию в новые версии firefox. developer.mozilla.org/ru/docs/Mozilla/Firefox_OS/Using_the_App_Manager. Тут же можно устанавливать разные версии эмуляторов, подключать девайсы и дебажить! Что очень удобно.
Так же нам нужен манифест приложения. Выглядит и пишется он очень просто.
Приведу пример:
{
"name": "PhotoFunia",
"description": "PhotoFunia is the best way to add a spark to your photos, make them special and more original.",
"launch_path": "/index.html",
"version" : "1",
"type": "privileged",
"developer": {
"name": "",
"email" : ""
},
"default_locale": "en",
"icons": {
"256": "/images/app_icon_256.png"
},
"permissions": {
"device-storage:pictures": {
"description": "Save result images",
"access": "readwrite"
},
"device-storage:sdcard": {
"description": "Save result images",
"access": "readwrite"
},
"mobilenetwork": {
"description": "Check for available connection"
},
"systemXHR": {
"description": "Need for internet connection"
}
}
}
Собственно говоря, засовываем все нужные файлы в .zip архив и отправляем в marketplace.firefox.com на проверку. Через пару дней (а может и к вечеру) приложение появится в маркете!
Что это было?
Я хотел донести до вас, что разработка под Firefox OS это очень просто, интересно и весело! Конечно, пока что, на этом денег не заработать. Но можно подумать, мы здесь ради денег собрались. Так что, берите IDE в руки, и создавайте приложения под Firefox OS!
Тем,
Все пожелания, а так же ошибки и недочеты принимаю в личку.