История одного приложения для MeeGo

Вместо введения или почему появилась эта статья
 

Всегда интересно принимать участие в развитии технологий, систем, в основе которых лежат новые идеи и подходы, быть частью сообщества, объединяющего людей с одними интересами и целями. С этой точки зрения операционная система MeeGo привлекает, с одной стороны, своей универсальностью - идеей применения на широком спектре устройств и, соответственно, разработкой приложений, адаптивных к использованию на различных видах устройств. С другой стороны, MeeGo базируется на хорошо известных и надежных технологиях, например, Qt Framework, OpenGL и др., и при этом объединяет в себе опыт, наработки других проектов (Maemo, Moblin, Debian, OpenSUSE, Fedora).

Начинать знакомство с новыми технологиями лучше всего с разработки не очень сложных и нагруженных приложений. Во время работы с MeeGo не хватало одной мелочи: кнопки для перезагрузки. Нам это показалось отличным вариантом: и несложно, и полезно - как раз то, что и требовалось. Хотелось получить привычную кнопку в области уведомлений для быстрого доступа, а, заодно, и опробовать систему публикации приложений в центре AppUp. Эта статья о том, что нам пришлось преодолеть при написании приложения, и  какие подводные камни встретились. 

Идея  Reboot Button

Давайте определим требования к разрабатываемому приложению. Как было сказано ранее, оно должно выполнять весьма ограниченный набор функций: выключение и перезагрузка, - и при этом быть легко доступным из графического окружения.

Что потребуется для разработки? В качестве инструментария был выбран QtFramework, поскольку графическое окружение MeeGo написано на его основе.

Для решения задачи легкого доступа к приложению мы выбрали самый простой вариант - иконка в system tray, в контекстном меню которой можно выбрать необходимое действие. Класс QSystemTrayIcon позволяет отображать произвольную иконку в области уведомлений, показывать всплывающие сообщения и отображать контекстное меню любой сложности. Также данный класс позволяет обрабатывать различные события. Рассмотрим фрагмент кода, иллюстрирующий создание иконки приложения в трее и добавления к ней контекстного меню:

Немножко кода

// Объект для значка программы в трее

QSystemTrayIcon* m_prebootIcon; 
// Контекстное меню иконки 
QMenu* m_pmenu; 
// Пункты меню 
QAction* m_rebootAction; 
QAction* m_shutDownAction;

Создадим объекты для каждого из пунктов меню

m_rebootAction = new QAction(tr("&Reboot"), this); 
m_shutDownAction = new QAction(tr("&Shut Down"), this);

Укажем, какие функции должны быть вызваны, если один из пунктов меню был выбран

connect(m_rebootAction, SIGNAL(triggered()), this, SLOT(reboot())); 
connect(m_shutDownAction, SIGNAL(triggered()), this, SLOT(shutDown()));

Создадим объект контекстного меню и добавим в него ранее созданные пункты

// Создаем объект QMenu 
m_pmenu = new QMenu(this); 
// Добавляем пункты меню для перезагрузки и выключения 
m_pmenu->addAction(m_rebootAction); 
m_pmenu->addAction(m_shutDownAction);

 Создадим объект QSystemTrayIcon и зададим для него контекстное меню, всплывающую подсказку(hint), а также отображаемую иконку.

// Создание объекта 
QSystemTrayIcon m_prebootIcon = new QSystemTrayIcon(this); 
// Добавляем иконке в трее контекстное меню 
m_pmenu m_prebootIcon->setContextMenu(m_pmenu); 
// Устанавливаем всплывающую подсказку для иконки в трее 
m_prebootIcon->setToolTip(tr("Reboot")); 
// Задаем изображение для иконки 
m_prebootIcon->setIcon(QPixmap(":/icons/reboot.png"));

Теперь покажем иконку в области уведомлений

m_prebootIcon->show();

Давайте рассмотрим функцию, вызываемую, если был выбран пункт Reboot:

void RebootTray::reboot()
{
//Вызов диалогового окна для подтверждения 
int r = QMessageBox::information(this, tr("Reboot"), tr("Would you like to reboot now?"),
                          QMessageBox::Yes, QMessageBox::No); 
if (r == QMessageBox::Yes) 
{ 
//Выполнение команды оболочки для перезагрузки 
system("sudo /sbin/reboot"); 
}
}

Следует подробнее остановиться на вызове функции system. Ее назначение - вызов команд операционной системы, заданных в качестве аргумента. В данном случае происходит запуск приложения /sbin/reboot от имени суперпользователя(потому как только у него есть на это права). Однако, если наше приложение будет запущено от имени обычного пользователя, то при выполнении данной команды появится окно с запросом пароля, что сделает использование программы менее удобным. Настройки команды sudo для каждого пользователя описаны в файле /etc/sudoers . Вот пример настроек для пользователя user, который имеет право выполнять любые команды используя sudo и введя свой пароль:

user ALL=(ALL) ALL

Чтобы избежать введения пароля, необходимо добавить в файл sudoers следущую запись:

ALL ALL=NOPASSWD: /sbin/reboot

Все вышесказанное справедливо и для функции, вызываемой при выборе пункта меню shutdown - необходимо заменить reboot на shutdown. Заметим, что для команды shutdown обязательно нужно указать ключ -h (останов или выключение после закрытия системы) и передать параметр time (время, когда нужно выполнить shutdown). Команда, вызываемая в нашем приложении, выглядит так:

system("sudo /sbin/shutdown -h now");

Еще нам потребовалось добавить окно уведомления о том, что приложение установлено и доступно для использования. При этом, уведомление должно показываться только в случаях первого запуска приложения и если нет параметра запуска в фоновом режиме. Со вторым условием справились, добавив в функцию main проверку на количество передаваемых параметров: если приложение запущено без параметров, то вызвать showNotification(). А вот как узнать, в первый ли раз запускается приложение? QSettings позволяет сохранять настройки пользователя в удобном формате: ключ - значение. QSettings очень прост в использовании: для начала нужно инициализировать объект QSettings, указав имя организации и приложения. Это бывает удобно, когда настройки используются одновременно в нескольких местах, вот здесь и помогают централизованно установленные имена организации и приложения. Как мы использовали возможности QSettings:

1. Объявляем переменные

QSettings m_settings;
bool m_isFirstLaunch;

2. Инициализируем в конструкторе (через список инициализации)

m_settings("aptu", "rebootbutton")

3. Читаем настройки в наши переменные

m_isFirstLaunch = m_settings.value("isFirstLaunch", true).toBool();

Здесь извлекаем значение с ключом, переданным в качестве первого параметра. Если же в объекте m_settings не содержится такого значения, то будет возвращено значение второго переданного параметра. Настройки базируются на использовании класса QVariant, что позволяет записывать значения различных типов (int, double, bool, QString, QRect и др.), поэтому при чтении настроек необходимо вызывать метод преобразования к нужному типу.

4. Теперь запишем настройки пользователя, чтобы при следующем запуске их восстановить

m_settings.setValue("isFirstLaunch", m_isFirstLaunch);

Если в m_settings еще нет записи с ключом “isFirstLaunch”, то такая будет создана с переданным значением, иначе - значение будет перезаписано.

Ну вот, мы всегда можем узнать, в первый ли раз запускается приложение и нужно ли показывать уведомляющее сообщение.

Сборка RPM пакетов

Итак, приложение готово, теперь нужно собрать rpm пакет. Есть два способа это сделать: собрать на своем компьютере, либо с помощью системы OBS (OpenSuse Build Service), но, в любом случае, нам потребуется spec файл (файл спецификаций, в котором указываются необходимые действия для сборки пакета, установки и удаления приложения).

Как это сделать вручную:

1. Устанавливаем утилиту rpmbuild. rpmbuild позволяет собрать пакет с использованием spec файла (за эту возможность отвечают ключи, начинающиеся с -b) или архива с исходным кодом (ключи -t).

2. В домашней директории создаем файл с именем .rpmmacros, где указываем расположение рабочей директории для процесса сборки пакета, расположение исходного кода приложения, .spec файлов, директории для хранения временных файлов и каталогов во время сборки приложения. А также указываем информацию, нужную для цифровой подписи пакета.

Пример .rpmmacros файла:

%_topdir /home/marchenko/BUILD_DI
%_sourcedir%{_topdir}/SOURCE
%_specdir%{_topdir}/SPECS
%_builddir%(_topdir)/BUILD

%_signature gpg
%_gpg_path /home/meego/.gnupg
%_gpg_name D7FAEB88
%_gpgbin /usr/bin/gpg

Если Вы раньше не использовали цифровую подпись, то сгенерировать ее можно командой

$ gpg --gen-key

В качестве gpg_name указываем идентификатор, узнать который можно с помощью gpg –list-keys (нам нужен идентификатор, указанный в секции pub после ”/”). Еще обычно в .rpmmacros указывают сборщика пакета (%packager) и поставщика (%vendor).

3. Вручную создаем следующую иерархию папок согласно определенному нами в .rpmmacros %{_topdir}:

BUILD_DIR
|--SOURCE
|--SPECS
|--RPMS
|--SRPMS
|--BUILD

Далее определенные нами макросы можно использовать в файле .spec, где указываются все необходимые команды для сборки пакета и его установки. Для нашего приложения spec-файл от примеров из документации (http://wiki.meego.com/Packaging/Guidelines, http://www.rpm.org/max-rpm/s1-rpm-build-creating-spec-file.html) будет отличаться только некоторыми моментами, связанными с тем, что мы собираем qt-приложение под операционную систему MeeGo. Например, в поле BuildRequires необходимо добавить зависимость от qt-devel. И не забудем сразу добавить в секцию %build вызов qmake.

%build
export PATH=/usr/lib/qt4/bin:$PATH
qmake PREFIX=%{_builddir}
make

Здесь следует отметить еще одну особенность, связанную с MeeGo: для этой операционной системы пакеты, связанные с разработкой, имеют суффикс не -dev, а -devel. Это небольшая особенность, которая по незнанию может отнять достаточно много Вашего времени. Еще в случаях, когда в Вашей программе используется какой-либо исполняемый файл, но Вы не знаете, к какому пакету он относится, может помочь команда:

$ rpm -qf /имя файла/

В результате выведется имя пакета, из которого был установлен данный файл.

Если Вы собираете приложение под ОС Meego, то потребуется еще создание .desktop файла. Он достаточно прост и содержит информацию о категории, к которой Вы относите свое приложение, а также информацию о расположении иконок и бинарных файлов. Для этого Вы создаете файл с именем Вашего приложения и расширением .desktop и помещаете нужную информацию(http://wiki.meego.com/Packaging/Guidelines#Desktop_files). И не забудьте добавить в зависимости BuildRequires в Вашем spec файле desktop-file-utils, а в секции %install - указать, что еще нужно установить .desktop файл:

%install
make install INSTALL_ROOT=$RPM_BUILD_ROOT

desktop-file-install \
--dir=%{_builddir}/%{name}-%{version}/ \
%{name}.desktop

Здесь еще одна оговорка, связанная с qt: можно сразу в .pro файле указать target.path, icon.path и т.д. (http://appdeveloper.intel.com/en-us/blog/2011/03/30/how-integrate-appup-sdk-and-create-rpm-package-meego-applications), тогда сгенерированный после вызова qmake Makefile можно использовать в Вашем файле спецификаций, например, в секции %install выполнить задачу install вместо того, чтобы прописывать все те же действия вручную.

Для нашей кнопки перезагрузки было бы удобно, если приложение запускалось каждый раз при старте системы. Для этого можно поместить .desktop файл в /etc/xdg/autostart/. Вот здесь один из самых интересных моментов. Дело в том, что явно в документации по использованию ОС MeeGo не описаны действия для случая, когда стороннее приложение должно запускаться при старте системы. Решение поместить наш desktop файл в /etc/xdg/autostart/ отвечает поставленной задаче, но насколько это соответствует Meego complience…об этом немного позже.

Подготовительный этап на этом закончен, далее пробуем собрать rpm пакет. Помещаем архив (традиционно это архивы tar.bz2 или tar.gz) с исходным кодом в BUILD_DIR/SOURCES/, а spec файл в BUILD_DIR/SPECS. Теперь выполняем команду rpmbuild:

$ rpmbuild -ba app.spec

С помощью rpmbuild можно собирать, как бинарные пакеты, так и пакеты с исходным кодом. Ключ -ba означает, что будут собраны пакеты обоих типов: бинарные пакеты автоматически будут помещены в директорию RPMS (например, RPMS/i386/myapp-0.1-1.i386.rpm, при этом от нас требуется только наличие директории RPMS, а поддиректории будут созданы автоматически во время выполнения команды rpmbuild), а пакеты с исходным кодом (app.src.rpm) - в SRPMS. src.rpm пакеты помимо исходного кода содержат spec файлы для возможности пересборки с целью лучшей адаптации к конкретной архитектуре и системному окружению.

Если после вызова команды rpmbuild в последней строке выведется + exit 0, значит в поддиректории каталога RPMS с именем целевой архитектуры лежит собранный rpm пакет. В случае, когда сборка удалась, пакет можно установить командой (для MeeGo):

$ sudo zypper in RPMS/i386/rebootbutton-0.1-1.i386.rpm

Имя пакета указываем полностью, потому нашего пакета в репозиториях еще нет, и утилита будет искать просто файл с указанным именем. А вот во время удаления ($ sudo zypper rm /имя/) полное имя указывать не нужно, так как приложение уже зарегистрировано в базе данных пакетов. К примеру, вызова

$ sudo zypper rm rebootbutton

будет достаточно.

Некоторые полезные команды:

$ rpm -ql /имя пакета/

выводит список всех установленных файлов для пакета, это верно только для тех файлов, которые были установлены в секции %install, а вот если Вы еще дополнительно установили какие-либо файлы в %post, то они отображены этой командой не будут.

$ sudo zypper info /имя пакета/

выведет информацию он пакете, преимущественно, будет совпадать с данными в секции introduction spec-файла.

Сборку пакета можно осуществить с помощью системы OBS (http://meego.e-werest.org/blog/meego/2939.html). Плюс в том, что для этого Вам не потребуется капризный в установке и настройке SDK, а только нужно предоставить системе архив с исходным кодом Вашей программы и spec-файл. Еще одно из многих преимуществ использования OBS для сборки - сборка осуществляется в “чистой” системе, т.е. то, что пакет собрался и установился на Вашем компьютере легко и непринужденно, еще не гарантирует, что так же будет на любом другом компьютере. Например, у Вас уже мог быть заранее установлен пакет qt-devel или какой-либо другой, необходимый для сборки, установки или функционирования вашего приложения и не установленный по умолчанию на любом другом девайсе c OS MeeGo. Для нашей кнопки после первой же попытки собрать в системе OBS добавили зависимость от qt-devel, но прежде этого в ветке #meego IRC-чата (http://webchat.freenode.net/) подсказали особенность, связанную с суффиксом -devel.

Публикация приложения

Регистрация

Для того, чтобы выкладывать свои приложения в магазин AppUp, необходимо пройти несложную процедуру регистрации. В первую очередь, необходимо заполнить небольшую анкету и ознакомиться с соглашением об использовании. Далее на указанную в процессе регистрации почту придет ключ регистрации. После его введения пользователь может сразу переходить к процессу загрузки приложения или создать организацию( или компанию), к которой он принадлежит. На этом регистрация завершается.

Meego complience

Если вы хотите опубликовать свое приложение в каталоге AppUp, необходимо, чтобы ваше приложение соответствовало критериям качества MeeGo complience, иначе в процессе валидации сотрудниками Intel AppUp Center приложение будет отклонено с пояснением причин этого. Их цель - обеспечить совместимость программ сторонних разработчиков с устройствами на базе MeeGo. Рассмотрим наиболее важные из них.

  • Именование пакетов. Пакет должен именоваться следующим образом: ru.ivanov-company.mysuperapp, где ivanov-company.ru - доменное имя разработчика, а mysuperapp - название приложения.
  • Зависимости. Пакеты могут иметь зависимости только от других пакетов, расположенных в основных репозиториях MeeGo. Если у пакета А есть зависимость от другого пакета Б из стороннего репозитория, то правильным выходом будет добавить содержимое пакета Б к файлам пакета А.
  • Интеграция с RPM. После удаления пакета система должна пребывать в состянии, в котором она была до его установки. Иными словами, при удалении через стандартный менеджер пакетов в системе не должно оставаться связанных с этим пакетом файлов(кроме файлов с настройками).
  • Использование файловой системы. Файлы пакета должны быть установлены в директорию /opt/packagename/, конфигурационные файлы - в директорию /etc/opt/packagename, а файлы, содержимое которых подвержено постоянному изменению(логи, файлы блокировок) - в директорию /var/opt/packagename .
  • Интеграция с графическим окружением. Каждый пакет должен иметь свой файл .desktop (кроме случаев, когда пакет - это набор библиотек). Ниже приведен пример минимального .desktop файла:
[Desktop Entry]
Version=1.0
Name=MyAppComment=MyApp 
descriptionType=Application
Icon=MyApp-logo-icon
Terminal=false
Exec=myapp
Categories=Utility;

В данном файле описываются основные сведения о приложении - версия, название, описание, тип, путь к иконке приложения, использовать ли терминал при запуске, команда для запуска и категория, к которой следует отнести приложение(имеются в виду категории, отображаемые во вкладке Applications).

Стоит отметить, что сотрудники Intel AppUp Center открыты для сотрудничества с Вами: после регистрации и добавления проекта нам на почту пришло письмо от сотрудника, осуществлявшего тестирование нашего приложения, с предложением о помощи в любых вопросах. Наш совет: не бойтесь задавать вопросы, Вам охотно пояснят все непонятные моменты. В первый раз наше приложение получило статус rejected. И если с частью несоответствий, связанных с Meego Complience, было все понятно, так как в документации достаточно подробно описаны все пункты требований, то с проблемами, относящимися к загрузке приложения через AppUp клиент, получилось с точностью, наоборот. Проблема оказалась в том, что во время установки кнопка, как ей и было положено, добавлялась в область уведомлений, но пользователь мог этого не затить, и складывалось впечатление, что возникли какие-то ошибки и установка не завершилась. На данный момент приложение находится в стадии бета-тестирования.

Сравнение Maemo и MeeGo

Maemo

Maemo - это дистрибутив Linux, адаптированный для интернет-планшетов от Nokia (N700,N800,N810) и мобильных телефонов(n900). Платформа является полностью открытой, за исключением небольшого количества проприетарного кода. Maemo поддерживает только устройства на базе процессоров архитектуры ARM(так как она не полностью открыта, то их всего четыре N700,800,810,900). Графическое окружение построено на базе GTK+ фреймворка Hildon, ставшего сегодня полноправной частью GNOME. Платформа использует пакеты приложений в формате deb, для управления ими используются apt и dpkg. Долгое время единственным средством разработки для Maemo была среда для кросс-компиляции Scratchbox(которая ориентирована на дистрибутивы linux, основанные на debian). Однако сегодня основной средой разработки для Maemo является Nokia Qt SDK - среда разработки, выпускаемая Nokia и позволяющая вести разработку как для desktop систем, так и для мобильных платформ Nokia.

Основным инструментом для распространения приложений для мобильных платформ Nokia(Maemo и Symbian) является магазин OVI. Кроме ПО, в OVI также можно купить различный мультимедиа контент - книги, музыку и фильмы. Также предоставляется ряд других сервисов:

  • Ovi Sync - синхронизация адресной книги между различными устройствами;
  • Ovi Maps - бесплатный картографический портал и одноименное навигационное ПО, предустановленное в современные устройства Nokia;
  • Ovi Mail - сервис электронной почты, имеющий удобный для мобильных устройств веб-интерфейс;
  • Ovi Files - файловый хостинг с удобным доступом для мобильных устройств;
  • Ovi Player - аналог Itunes для платформ Nokia.

Чтобы присоединиться к программе OVI, необходимо заплатить взнос в размере 1 €, кроме того, Nokia будет высчитывать 30% от всех продаж разработчика.

MeeGo

Meego - совместный Open Source проект компаний Intel и Nokia, объединяющий опыт и наработки платформ Moblin (Intel) и Maemo (Nokia). Основная идея - мульти-платформенность: операционная система предназначена для работы на широком спектре устройств (смартфоны, планшеты, нетбуки, компьютеры, телевизионные приёмники и информационно-развлекательные системы). На данный момент поддерживаются архитектуры Intel Atom и ARM.

Если рассматривать архитектуру ОС MeeGo с точки зрения разделения на слои, то выделяют три основных уровня:
1) User Experience Layer (Уровень пользовательского интерфейса) - рассчитан на поддержку различных аппаратных платформ. Предоставляет набор фреймворков (Application Framework) для каждого типа UX (User eXperience), например, Clutter и MX Libraries для Netbook Application Framework, Meego Touch Framework для Handset Application Framework.

2) Application API Layer - определяет MeeGo API - интерфейс для разработки приложений основан на Qt 4.7, Qt Mobility 1.0, Open GL ES 1.1.

3) Core OS Layer - ядро Linux Kernel 2.6.35 или новее, промежуточное ПО, драйверы для аппаратного обеспечения.

Что касается графического окружения, то в Meego включена поддержка графического 2D.3D стека, аппаратного ускорения графики, а также поддержка различных устройств ввода (кнопки на аппаратуре (кнопка power, камера, кнопки для управления громкостью и др), qwerty-клавиатура, touch screen и др.). В качестве оконной системы используется реализация X11 Window System со специфичными для архитектуры драйверами, патчами и настройками(конфигурациями).

Приложения для MeeGo можно разрабатывать с использованием Qt или Qt Quick, к тому же Meego SDK включает среду для разработки Qt Creator. Основываясь на мульти-платформенности Meego, разработчики могут создавать приложения, которые будут работать сразу на нескольких видах устройств. На уровне пакетов используется формат rpm и основанный на yum менеджер пакетов zypper. zypper во многом похож на apt (использование нескольких репозиториев, обновление, установка/удаление пакетов и т.д.).

Специальная программа для разработчиков центра Intel AppUp открывает Ваши приложения миллионам пользователей. На данный момент через центр AppUp можно распространять приложения для ОС Meego и Windows как на платной основе, так и бесплатно. При этом регистрировать можно не только приложения, но и какие-либо готовые компоненты, которые могут быть полезны другим разработчикам. Программа нацелена максимально помочь разработчикам: предоставляются возможность использования SDK, каталоги компонентов для разработчиков, интеграция с сообществом разработчиков, а также участие в Application Labs, где Вы можете повысить свои навыки в разработке приложений для центра AppUp и поделиться собственными знаниями с другими.

На данный момент регистрация в цетре Intel AppUp бесплатная, но с течением времени это будет стоить 99$ в год.

На основе данного сравнения можно сделать вывод о том, что хотя система MeeGo является полностью открытой, для нее пока нет таких же удобных сервисов,как те, что Nokia предоставляет для Maemo.

Заключение

В этой статье нам хотелось поделиться полученным опытом. Изначально казалось, что разработка приложения Reboot Button займет считанные часы, на практике все оказалось немного сложнее. Надеемся, что наша история одной кнопки будет полезна тем, кто только собирается опубликовать свое приложение в центре AppUp. Желаем успехов!

Links

1. rpmbuild http://linux.die.net/man/8/rpmbuild

2. rpmmacros http://fedoraproject.org/wiki/Packaging/RPMMacros

3. gpg http://linux.die.net/man/1/gpg

4. creating spec file http://www.opennet.ru/docs/RUS/rpm_guide/39.html

5. Meego complience http://appdeveloper.intel.com/en-us/article/making-your-application-meego-compliant

6. AppUp validation http://appdeveloper.intel.com/en-us/article/validation-guidelines

7. packaging http://appdeveloper.intel.com/en-us/article/meego-packaging-and-compliance-guidelines

8. http://wiki.meego.com/SDK/Docs/1.2/MeeGo_SDK_1.2_Preview_for_Linux/Create_RPM_Package_For_MeeGo_Application

9. desktop files http://wiki.meego.com/Packaging/Guidelines#Desktop_files

Светлана Марченко, Марк Заславский