...
...

Программируем смартфоны Symbian Series 60. Приложение HelloWorld

Корпорация Nokia выпускает огромное количество смартфонов. Однако информации о программировании смартфонов Nokia на русском языке крайне мало. Этой серией статей я хочу помочь тем, кто хочет начать писать программы для смартфонов на базе платформы Symbian Series 60 на языке C++.

О платформе


Для начала несколько слов о платформе. Series 60 построена на базе операционной системы Symbian. Базовая система Symbian не имеет интерфейса пользователя. При ее разработке предполагалось, что интерфейс производители устройств будут делать сами. В Nokia сделали целых три варианта интерфейса: Series 60, Series 80 и Series 90. На базе Series 90 выпущено только одно устройство — Nokia 7710 — и больше не будет. На базе Series 80 работают Nokia 9300, Nokia 9500 и все их предшественники серии 9xxx. По информации Nokia, скорее всего, больше устройств на Series 80 не будет, и наследники Nokia 9300/9500 будут работать на базе Series 60.

Series60 (далее S60) — это самая массовая и перспективная платформа Nokia. Количество устройств на базе разных редакций S60 начиная с 7650 и до современных N- и E-серий (полный список см. ниже) выпущено больше, чем КПК и смартфонов на других платформах (Windows Mobile, Palm, Linux) вместе взятых. Кроме Nokia, на базе S60 выпускают устройства и другие производители: Siemens SX1, Samsung D720 и D730 и др. На сегодняшний день существует три редакции платформы S60 — соответственно, первая, вторая и третья. Вторая редакция имеет четыре варианта — так называемые Feature Pack (S60v2, S60v2FP1, S60v2FP2 и S60v2FP3). Программы для первой и второй редакции имеют обратную совместимость, т.е. программа, написанная для первой редакции, будет работать и на второй. Однако при переходе на третью редакцию совместимость теряется. Таким образом, если вы захотите сделать программу для продажи, то вам придется ее делать в двух вариантах: для первой/второй редакции и для третьей. Мы с вами рассмотрим оба варианта.

SDK

Для разработки программ нам понадобится Series 60 SDK. Его можно скачать с сайта forum.nokia.com. Там будет несколько версий SDK. Каждая из них соответствует определенной версии S60. Вот таблица соответствия:


S60 SDKСмартфоны
1st7610, 3600, 3620, 3650, 3660, N-Gage, N-Gage QD
2nd6600, 6620
2ndfp13230, 6260, 6670, 7610
2ndfp26630, 6680, 6681, 6682
2ndfp3N70, N72, N90
3rd3250, 5500, E50, E60, E61, E62, E70, N71, N73, N75, N80, N91, N92, N93
3rdfp1N95


Сначала мы рассмотрим программирование для первой и второй редакций S60. Я вам рекомендую скачать SDK 2nd. Для разработки программ можно использовать CodeWarrior Nokia Edition (для разных версий SDK предназначены разные версии CodeWarrior), Microsoft Visual Studio 6.0, Microsoft Visual Studio 2003 (но не Visual Studio 2005) или Eclipse (этот вариант я вам не рекомендую, так как будет много лицензионных проблем, если вы захотите продавать свою программу). Если вы выберете CodeWarrior, то вам понадобится SDK, помеченное как CW (для первой редакции SDK CW-версии нет), а для Visual Studio понадобится версия, помеченная как WINS. Начиная с SDK 3 обе версии объединены. Итак, если у вас есть SDK и какая-либо интегрированная среда (хотя можно обойтись и одним SDK), то давайте приступим.

Установка и настройка SDK

Для начала надо установить SDK. Устанавливайте его на тот же диск, где у вас будут располагаться ваши проекты. По умолчанию предлагается диск C:. Соглашайтесь — впоследствии будет меньше проблем. Если же вы захотите поставить SDK в другое место, то в новом пути ни в коем случае не должно быть пробелов, иначе все встанет нормально, но не будет работать. В ходе установки вам предложат поставить Active Perl — обязательно соглашайтесь — без Perl вы ничего не сможете скомпилировать. Также для некоторых утилит из SDK 1 и 2.х нужна Java 1.3.1 (именно 1.3.1, так как с другими — например, 1.4 или 1.5 — некоторые утилиты просто не запускаются). Однако без этих утилит можно прекрасно обойтись, поэтому на Java можно не обращать внимания. После того как вы установили все пакеты, необходимо прописать некоторые переменные среды.

Во-первых, надо добавить переменную EPOCROOT и прописать в ней путь к папке, в которой расположена папка epoc32, притом в пути не должно быть имени диска. Для SDK 2 EPOCROOT=\Symbian\7.0s\Series60_v20\. Во-вторых, в переменную среда PATH надо добавить пути к папке bin среды ActivePerl, к папкам epoc32\tools и epoc32\gcc\. Важное замечание: перед этими путями в PATH не должно быть путей с пробелами, поэтому рекомендую добавлять их в начало списка PATH. Если вы будете использовать Visual Studio, надо прописать еще пути к папкам bin этого пакета. После установки SDK перейдем непосредственно к программированию.

Приложение Hello world для Series60 1st и 2nd edition

Напишем традиционное приложение Hello world для первой и второй редакции Series60. Symbian — объектноориентированная операционная система (ОС). Все ее элементы представляются в виде классов. При написании своего приложения вы должны породить свои классы от следующих классов ОС: CAknApplication — класс, описывающий ваше приложение;

CAknDocument — класс, описывающий документ приложения;
CAknAppUi — класс, реализующий интерфейс пользователя (работа с меню, заголовками, различными элементами управления);
CCoeControl — рабочая область вашего приложения. Вместо этого класса вы можете использовать любой стандартный элемент (список, редактор текста и т.п.), так как они все порождены от CCoeControl.

Обычно каждый класс реализуют в отдельном файле. Мы будем поступать именно так. Имя файла будет совпадать с именем класса (только без буквы C вначале).

Файл HelloWorldApplication.h

#ifndef __HELLOWORLD_APPLICATION_H__
#define __HELLOWORLD_APPLICATION_H__
#include <aknapp.h>
class CHelloWorldApplication : public CAknApplication {
public: TUid AppDllUid() const;
protected: CApaDocument* CreateDocumentL();
};
#endif // __HELLOWORLD_APPLICATION_H__

Файл HelloWorldApplication.cpp

#include "HelloWorldDocument.h"
#include "HelloWorldApplication.h"
const TUid KUidHelloWorldApp = {0x10005B91};
TUid CHelloWorldApplication::AppDllUid() const {
return KUidHelloWorldApp;
}
CApaDocument* CHelloWorldApplication::CreateDocumentL() {
return (static_cast<CApaDocument*>( CHelloWorldDocument::NewL(*this)));
}

В классе CHelloWorldApplication мы переопределяем 2 функции: AppDllUid и CreateDocumentL. Функция AppDllUid возвращает уникальный идентификатор приложения (UID). На этапе разработки приложения вы можете взять в качестве UID любое значение в диапазоне от 0x00000000 до 0x0FFFFFFF. Однако с таким UID приложение запустится только на эмуляторе. Для финальной версии вы должны получить уникальный UID в компании Symbian. Для Series60 1st and 2nd выдаются значения 0x10000000 до 0x1FFFFFFF. Информация о порядке получении Uid доступна на сайте сайт . В данном примере используется UID от примера HelloWorld из SDK. Вторая функция CreateDocumentL создает главный документ и возвращает указатель на него.

Файл HelloWorldDocument.h

#ifndef __HELLOWORLD_DOCUMENT_H__
#define __HELLOWORLD_DOCUMENT_H__
#include <akndoc.h>
class CEikApplication;
class CHelloWorldDocument : public CAknDocument {
public: static class CHelloWorldDocument* NewL(CEikApplication& aApp);
private: CHelloWorldDocument(CEikApplication& aApp);
void ConstructL();
public: CEikAppUi* CreateAppUiL();
};
#endif // __HELLOWORLD_DOCUMENT_H__

Файл HelloWorldDocument.cpp

#include "HelloWorldAppUi.h"
#include "HelloWorldDocument.h"
CHelloWorldDocument* СHelloWorldDocument::NewL(CEikApplication& aApp) {
CHelloWorldDocument* self = new (ELeave) CHelloWorldDocument(aApp);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop(self);
return self;
}
CHelloWorldDocument::CHelloWorldDocument( CEikApplication& aApp) : CAknDocument(aApp) {;}
void CHelloWorldDocument::ConstructL() {;}
CEikAppUi* CHelloWorldDocument::CreateAppUiL() {
return (static_cast <CEikAppUi*> (new (ELeave) CHelloWorldAppUi));
}

Класс CHelloWorldDocument содержит двухфазный конструктор (конструктор класса и функция ConstructL()) и создается с помощью статической функции NewL. Зачем это надо, зачем в конце имен функций стоит буква L и о том, что такое CleanupStack и ELeave, вы узнаете в следующей статье цикла.

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

Файл HelloWorldAppUi.h

#ifndef __HELLOWORLD_APPUI_H__
#define __HELLOWORLD_APPUI_H__
#include <aknappui.h>
class CHelloWorldAppView;
class CHelloWorldAppUi : public CAknAppUi {
public: void ConstructL();
CHelloWorldAppUi();
~CHelloWorldAppUi();
public: void HandleCommandL(TInt aCommand);
private: CHelloWorldAppView* iAppView;
};
#endif // __HELLOWORLD_APPUI_H__

Файл HelloWorldAppUi.h

#include <avkon.hrh>
#include <aknnotewrappers.h>
#include "HelloWorldAppUi.h"
#include "HelloWorldAppView.h"
#include "HelloWorld.hrh"

CHelloWorldAppUi::CHelloWorldAppUi() {;}
void CHelloWorldAppUi::ConstructL() {
BaseConstructL();
iAppView = CHelloWorldAppView::NewL(ClientRect());
AddToStackL(iAppView);
}
CHelloWorldAppUi::~CHelloWorldAppUi() {
if (iAppView) {
iEikonEnv->RemoveFromStack(iAppView);
delete iAppView;
}
}

void CHelloWorldAppUi::HandleCommandL(TInt aCommand) {
switch(aCommand) {
case EEikCmdExit: case EAknSoftkeyExit:
Exit(); break;
}
}

В конструкторе ConstructL класса CHelloWorldAppUi создается элемент управления, занимающий всю видимую клиентскую область приложения. Клиентская область начинается за заголовком окна и заканчивается программными кнопками внизу экрана. Координаты этой области возвращает функция ClientRect. Созданный элемент добавляется в стек элементов интерфейса. Вторая функция HandleCommandL — это обработчик сообщений от меню. У нас в меню будет только одна команда — закрыть приложение. По этой команде вызывается функция Exit, закрывающая приложение.

Файл HelloWorldAppView.h

#ifndef __HELLOWORLD_APPVIEW_H__
#define __HELLOWORLD_APPVIEW_H__
#include <coecntrl.h>
class CHelloWorldAppView : public CCoeControl {
public: static CHelloWorldAppView* NewL(const TRect& aRect);
private: CHelloWorldAppView();
void ConstructL(const TRect& aRect);
public: void Draw(const TRect& aRect) const;
};
#endif // __HELLOWORLD_APPVIEW_H__

Файл HelloWorldAppView.cpp

#include <coemain.h>
#include "HelloWorldAppView.h"
CHelloWorldAppView* CHelloWorldAppView::NewL(const TRect& aRect) {
CHelloWorldAppView* self = new (ELeave) CHelloWorldAppView;
CleanupStack::PushL(self);
self->ConstructL(aRect);
CleanupStack::Pop(self);
return self;
}
CHelloWorldAppView::CHelloWorldAppView() {;}
void CHelloWorldAppView::ConstructL(const TRect& aRect) {
CreateWindowL();
SetRect(aRect);
ActivateL();
}
void CHelloWorldAppView::Draw(const TRect& /*aRect*/) const {
CWindowGc& gc = SystemGc();
TRect rect = Rect();
gc.Clear(rect);
gc.UseFont(AknLayoutUtils::FontFromId(
EAknLogicalFontPrimaryFont));
gc.DrawText(_L("Hello World!"), TPoint(8, 8));
}

CHelloWorldAppView — это рабочая область нашего приложения. В конструкторе ConstructL мы создаем окно, устанавливаем размер и местоположение области и активируем его с помощью ActivateL. Если класс не активировать, то он не будет прорисовываться и не будет реагировать на события. Вторая функция, которую мы переопределяем, — это Draw. Она будет вызываться системой каждый раз, когда надо перерисовать рабочую область. Здесь мы выводим надпись "Hello world". Для этого мы сначала получаем контекст рисования gc, затем очищаем окно, выбираем шрифт, которым будем выводить надпись, и рисуем текст.

Мы определили все необходимые классы. Работа программы начинается с создания экземпляра класса CHelloWorldApplication. Это делается следующим образом.

Файл helloworld.cpp

#include "HelloWorldApplication.h"
GLDEF_C TInt E32Dll(TDllReason /*aReason*/) {
return KErrNone;
}
EXPORT_C CApaApplication* NewApplication() {
return (static_cast<CApaApplication*>(new CHelloWorldApplication));
}

Эти две функции должны обязательно присутствовать в вашей программе. При запуске программы система вызывает функцию NewApplication, в которой создается класс CHelloWorldApplication и возвращается указатель на него.
Все, все необходимые классы и функции мы определили. Осталось описать ресурсы приложения. Файл ресурсов имеет расширение .rss и в нашем примере будет иметь следующий вид:

Файл helloworld.cpp

#include <eikon.rh>
#include <avkon.rh>
#include <avkon.rsg>

NAME HELL
RESOURCE RSS_SIGNATURE { }
RESOURCE TBUF r_default_document_name { buf=""; }
RESOURCE EIK_APP_INFO {
menubar = r_helloworldbasic_menubar;
cba = R_AVKON_SOFTKEYS_OPTIONS_EXIT;
}
RESOURCE MENU_BAR r_helloworldbasic_menubar {
titles = {
MENU_TITLE{ menu_pane = r_helloworldbasic_menu;}
};
}
RESOURCE MENU_PANE r_helloworldbasic_menu {
items = {
MENU_ITEM {
command = EAknSoftkeyExit;
txt = "Exit";
}
};
}

Файлы ресурсов играют очень важную роль в приложениях S60. NAME определяет краткое имя приложения. RSS_SIGNATURE обычно пустое, но, тем не менее, выкинуть его нельзя — этот ресурс всегда должен быть вторым. Третьим ресурсом обязательно должна быть строка имени главного документа (обычно пустая строка). Четвертым параметром обязательно должна идти информация о приложении EIK_APP_INFO. Здесь определяется, какое меню будет вызываться по кнопке Options (поле menubar) и какие кнопки будут связаны с софт клавишами (поле сba). В нашем примере это кнопки Options и Exit. Далее идут остальные ресурсы в произвольном порядке. В нашем примере это описание меню.
После того как мы создали файл ресурсов, осталось только создать файл проекта и откомпилировать его. Файл проекта должен иметь расширение .mmp.

Файл HelloWorld.mmp

TARGET HelloWorld.app
TARGETTYPE app
UID 0x100039CE 0x10005B91
TARGETPATH \system\apps\helloworld

SOURCEPATH .
SOURCE HelloWorld.cpp
SOURCE HelloWorldApplication.cpp
SOURCE HelloWorldAppView.cpp
SOURCE HelloWorldAppUi.cpp
SOURCE HelloWorldDocument.cpp

SOURCEPATH .
RESOURCE HelloWorld.rss

USERINCLUDE .
SYSTEMINCLUDE \epoc32\include

LIBRARY euser.lib
LIBRARY apparc.lib
LIBRARY cone.lib
LIBRARY eikcore.lib
LIBRARY avkon.lib

Параметр TARGET определяет имя приложения, TARGETTYPE его тип. В S60 1st и 2nd тип приложения должен быть app. UID определяет идентификаторы приложения. Первый называется UID2 и всегда должен быть 0x100039CE, второй называется UID3 и должен совпадать с UID, который возвращает функция AppDllUid класса приложения (в нашем примере класса CHelloWorldApplication). TARGETPATH определяет папку, в которой располагается приложение. Далее идут имена файлов исходных текстов. SOURCEPATH определяет, где они располагаются относительно файла проекта. Затем идет имя файла ресурсов, затем пути, где располагаются пользовательские и системные заголовочные файлы, и в конце перечисляются используемые библиотеки. Если вы используете CodeWarrior, экспортируйте в него файл HelloWorld.mmp и откомпилируйте получившийся проект. Если вы используете Visual Studio, то вам надо создать еще один файл. Он имеет имя bld.inf и содержит всего две строчки:
PRJ_MMPFILES
HelloWorld.mmp

Затем надо создать проект для Visual Studio. Для этого в командной строке набираем: bldmake bldfiles. В результате у вас должен появиться файл ABLD.BAT. Затем набираем в командной строке: makmake HelloWorld.mmp vс6, если вы используете Visual Studio 6.0, или makmake HelloWorld.mmp vс7, если вы используете Visual Studio 2003. В результате у вас появится файл проекта для Visual Studio. Запустите Visual Studio, выберите получившийся проект и скомпилируйте его. Теперь запустите его. При первом запуске среда попросит выбрать запускаемый файл. Надо выбрать файл эмулятора — для SDK 2 это \Symbian\7.0s\Series60_v20\Epoc32\release\wins\udeb\epoc.exe. В результате запустится эмулятор. В списке приложений найдите HelloWorld и запустите. Теперь, если у вас есть смартфон, давайте скомпилируем и установим нашу программу на устройство S60. Для компиляции набираем в командной строке (в том каталоге, где у нас расположен файл ABLD.BAT): abld build armi urel. Скомпилированный будет находиться в папке epoc32\release\thumb\urel. После компиляции надо создать установочный файл с расширением .sis. Для этого сначала мы должны описать состав sis-файла. Для этого создается текстовый файл с расширением .pkg.

Файл HelloWorld.pkg

&EN
#{"HelloWorld"},(0x10005B91),1,0,0
(0x101F7960), 0, 0, 0, {"Series60ProductID"}
" \SYMBIAN\7.0s\Series60_v20\epoc32\release\thumb\urel\HelloWorld.APP" — "!:\system\apps\HelloWorld\HelloWorld.app"
"\SYMBIAN\7.0s\Series60_v20\epoc32\data\z\system\apps\HelloWorld\HelloWorld.rsc" — "!:\system\apps\HelloWorld\HelloWorld.rsc"

В первой строчке задается язык программы (английский). Во второй, начинающейся с #, описывается наше приложение. Первым элементом идет название приложения, вторым (после запятой) — его UID, далее — старший и младший номера версии программы, и последним — номер сборки. В третьей строке описывается версия системы, для которой предназначена программа. В этой строке можно изменять только первый элемент. Это UID операционной системы. Допустимы следующие варианты:


0x101F7960S60 2nd Edition
0x101F9115S60 2nd Edition Feature Pack 1
0x10200BABS60 2nd Edition Feature Pack 2
0x102032BFS60 2nd Edition Feature Pack 3
0x101F7961S60 3rd Edition


Далее перечисляются файлы, устанавливаемые на устройство. В каждой строчке сначала указывается исходный файл, ставится минус и указывается, куда устанавливать этот файл на устройстве. Восклицательный знак означает, что имя диска выбирается при установке. После того как мы создали pkg- файл, набираем в командной строке: makesis.exe HelloWorld.pkg. В результате появиться файл HelloWorld.sis. Ну, а дальше копируйте его на устройство, устанавливайте и запускайте программу.

Алексей Аношенко

© Компьютерная газета

полезные ссылки
Аренда ноутбуков