Предварительные замечания
Здравствуйте. Хотелось бы прежде всего поздравить всех с наступившим Новым Годом и сразу перейти к делу. Эта статья будет посвящена вопросу создания простых программ для Bada — операционной системы для мобильных устройств, которые выпускает корейская Samsung. Заранее хочу сказать, что под катом простой рецепт изготовления простого приложения без пространных рассуждений о оптимальности и стройности кода.
На Хабре уже есть довольно обстоятельные статьи, которые посвящены Bada, например эта и эта, но вот пошагового описания создания приложения для новичков нет. Кому интересно — прошу под кат.
Тема для написания возникла случайно — знакомый активно занимается установлением границ земельных участков при помощи GPS в геодезической фирме; специфика работы (откуда взялась — хороший вопрос) состоит в том, что получаемые геодезические координаты (позволю себе пояснить, что имеется ввиду широта и долгота) надо переводить в прямоугольные прямо после их получения. На фирме ноутбуков, способных выдержать 8 часовой рабочий день нет и не планируется, зато знакомый недавно купил себе активно продвигаемый в Украине смартфон от Samsung с операционной системой Bada, далее последовал вопрос — если можно скачать игры для телефона, то нет ли приложения, способного решить описанную чисто инженерную задачу? Такого приложения, конечно, не нашлось, и я получил любезное предложение такое написать за известную плату.
Инструментарий
Для разработки приложений для Bada используется Bada SDK 1.2.0, скачать который можно, зарегистрировавшись на сайте для разработчиков Bada. В этом процессе нет ничего сложного, но вот на этапе выбора файлов для загрузки я, честно сказать, «протупил», скачав только SDK, а так называемый языковой пакет (language pack) нет, понадеявшись, что, как написано на сайте, его загрузка произойдет после установки SDK автоматически. Однако, на этапе установки что-то шло не так, установка продолжалась без загрузки языкового пакета, что в дальнейшем приводило к невозможности ни выполнить построение (build), ни, тем более, запустить приложение.
Загрузив SDK и языковой пакет, их следует положить в одном месте, и при установке SDK или при её модификации, установщик подхватит языковой пакет и выполнит инсталляцию его самостоятельно.
Собственно создание приложения
Среда разработки основана на Eclipse, используемый язык программирования С++. Начнем с того, что перейдем к созданию нового приложения очевидной командой File -> New -> bada Application Project. В появившемся окне следует определить название проекта в поле Project name, в списке Project Type выбрать тип приложения — bada Form Based Application.
Далее среда попросит определить технические параметры устройства, для которого будет предназначено приложение (разрешение экрана, наличие GPS и т.д.). Эти параметры определяются в специальном файле манифеста Manifest.xml, который можно выбрать среди файлов SDK, создать и загрузить с сайта Bada Developers или создать свой. Не мудрствуя лукаво, я использовал файл манифеста из папок с примерами Samples, которые устанавливаются вместе с SDK, поскольку мое приложение не требовало наличия «наворотов», а было важно только разрешение экрана.
Далее среда попросит указать корневую директорию SDK, с этим проблем возникнуть не должно. После выбора в списке появится название модели устройства и его API, которое будет использоваться в дальнейшем для программирования.
Следующий шаг — определение имени формы по умолчанию, которая появится сразу после запуска приложения. По этому имени будут сгенерированы *.h и *.cpp файлы.
Далее потребуется определить добавлять или нет поддержку авто-масштабирования и наилучшее разрешение экрана, если эта возможность будет добавлена. Я так понимаю, что это лучше сделать, поскольку приложение сможет работать на разных устройствах с разным разрешением и его не придется переделывать.
Следующее звено в длинной цепочке настроек — определение Application ID, для удостоверения функций приложения, если ему требуется выполнять чтение и запись в защищенные папки. Мне это не требовалось, поэтому я этот шаг опустил.
Почти конец — определение очевидных свойств приложения Name, Vendor и Description.
И вот уже почти почти конец — это определение необходимых конфигураций для разработки: рекомендую оставить все по умолчанию, а сейчас наиболее важной конфигурацией является Simulator-Debug — это возможность отладки кода на симуляторе устройства, Target-Release — конфигурация для компилирования приложения пред выгрузкой его на реальное устройство.
Последний шаг — сведенная по всем настройкам информация в окне Итогов (Summary), которую можно еще раз прочитать и убедиться, что все выбрано правильно.
После финала настроек будущее приложение появляется в Project Explorer, щелчок по которому приводит к раскрытию файлов ресурсов в окне Resources и появлению следующего окна:

После щелчка на форме (и вообще на любом элементе управления) на вкладке Properties можно изменить очевидные свойства приложения, такие как текст в заголовке, цвет фона, наличие и название софт-клавиш.
Я сразу перенес уже готовую кнопку, которую любезно сгенерировала среда, пониже и написал на ней Calculate. Располагая GUI Editor легко получить следующий интерфейс, который хорошо подходит для моей задачи (полагаю, что процесс переноса элементов управления на форму и установки их свойств никаких затруднений не вызывает); в качестве подписей к полям ввода использован очевидный Label, в качестве полей ввода — EditField; для показа результата вычислений также использованы Label, поскольку их редактирование не предусмотрено:

Теперь собственно кодирование.
Выполняющееся приложение проходит несколько этапов (Помощь очень подробная, там можно узнать детали), сейчас в первую очередь нас интересует этап иницализации приложения, за который отвечает метод OnInitializing(void) класса формы, который можно найти в файле <ВАША_ФОРМА>.cpp в папке src проекта.
По умолчанию этот метод выглядит следующим образом:
result
Form1::OnInitializing(void)
{
result r = E_SUCCESS;
// TODO: Add your initialization code here
// Get a button via resource ID
__pButtonOk = static_cast<Button *>(GetControl(L"IDC_BUTTON_OK"));
if (__pButtonOk != null)
{
__pButtonOk->SetActionId(ID_BUTTON_OK);
__pButtonOk->AddActionEventListener(*this);
}
return r;
}
Здесь создана кнопка с идентификатором __pButtonOk, которая была сгенерирована средой. В папке inc находится заголовочный файл формы, в котором в секцию protected следует добавить новое поле, которое будет соответствовать второй кнопке Clear, а также идентификатор события (Id Action) кнопки в виде целочисленной константы, который будет предназначен для определения того, какая кнопка формы была нажата в методе OnActionPerformed. Также я сюда добавил специальные константы, нужные для вычислений.
Я сделал это так:
protected:
static const int ID_BUTTON_OK = 101;
// new action ID for button CLEAR
static const int ID_BUTTON_CLEAR=102;
// geodetic constants
static const int a=6378137;
static const float alfa=1/298.257223563;
Osp::Ui::Controls::Button *__pButtonOk;
// new field: button
Osp::Ui::Controls::Button *__pButtonClear;
// new fields: edits and labels
Osp::Ui::Controls::EditField *__pB_deg;
Osp::Ui::Controls::EditField *__pB_min;
Osp::Ui::Controls::EditField *__pB_sec;
Osp::Ui::Controls::EditField *__pL_deg;
Osp::Ui::Controls::EditField *__pL_min;
Osp::Ui::Controls::EditField *__pL_sec;
Osp::Ui::Controls::EditField *__pH;
Osp::Ui::Controls::Label *__pX;
Osp::Ui::Controls::Label *__pY;
Теперь в методе OnInitializing(void) можно создать кнопку Clear и назначить ей Id Action и «слушатель» Event Listener. Аналогично поступим и с EditField и с Label, не добавляя к ним, однако, Id Action и Event Listener поскольку их реакция на действия пользователя в нашем простом приложении не предусмотрена (IDC_BUTTON1, IDC_EDITFIELD1 и т.д. — значения свойства Name для кнопки).
__pButtonOk = static_cast<Button *>(GetControl(L"IDC_BUTTON_OK"));
__pButtonClear = static_cast<Button *>(GetControl(L"IDC_BUTTON1"));
if (__pButtonOk != null)
{
__pButtonOk->SetActionId(ID_BUTTON_OK);
__pButtonOk->AddActionEventListener(*this);
}
if (__pButtonClear != null)
{
__pButtonClear->SetActionId(ID_BUTTON_CLEAR);
__pButtonClear->AddActionEventListener(*this);
}
// Get fields and labels
__pB_deg = static_cast<EditFields *>(GetControl(L"IDC_EDITFIELD1"));
__pB_min = static_cast<EditFields *>(GetControl(L"IDC_EDITFIELD2"));
__pB_sec = static_cast<EditFields *>(GetControl(L"IDC_EDITFIELD3"));
__pL_deg = static_cast<EditFields *>(GetControl(L"IDC_EDITFIELD4"));
__pL_min = static_cast<EditFields *>(GetControl(L"IDC_EDITFIELD5"));
__pL_sec = static_cast<EditFields *>(GetControl(L"IDC_EDITFIELD6"));
__pH = static_cast<EditField *>(GetControl(L"IDC_EDITFIELD7"));
На данном этапе уже можно запустить приложение и посмотреть как в симулятор вводятся данные, но пользы, пока не обрабатываются события нажатия на кнопки, конечно нет.
Чтобы решить эту задачу, используется метод класса формы OnActionPerformed(const Osp::Ui::Control& source, int actionId). Как видно, вторым параметром является уже упоминаемый выше Action Id. При выполнении приложения «прослушиваются» события нажатия кнопок и назначенный им Action Id передается в OnActionPerformed(const Osp::Ui::Control& source, int actionId). Если здесь предусмотрена обработка для такого идентификатора, она и выполняется.
В OnActionPerformed есть заготовка switch для анализа параметра actionId, её удобно использовать. Далее я позволю себе привести код этого метода с подробными комментариями:
void
Form1::OnActionPerformed(const Osp::Ui::Control& source, int actionId)
{
const int MAX_BUFF_SIZE = 512;
switch(actionId)
{
case ID_BUTTON_OK:
// реакция на нажатие кнопки Calculate
{
// получаем строковое представление градусов, минут и секунд широты и ...
String B_deg_str(__pB_deg->GetText());
String B_min_str(__pB_min->GetText());
String B_sec_str(__pB_sec->GetText());
//...долготы
String L_deg_str(__pL_deg->GetText());
String L_min_str(__pL_min->GetText());
String L_sec_str(__pL_sec->GetText());
// ... высоты
String H_str(__pH->GetText());
double B_deg,B_min,B_sec;
double L_deg,L_min,L_sec;
double H;
// преобразование строковых представлений чисел в вещественную форму для вычислений
result r1=Double::Parse(B_deg_str,B_deg);
result r2=Double::Parse(B_min_str,B_min);
result r3=Double::Parse(B_sec_str,B_sec);
result r4=Double::Parse(L_deg_str,L_deg);
result r5=Double::Parse(L_min_str,L_min);
result r6=Double::Parse(L_sec_str,L_sec);
result r7=Double::Parse(H_str,H);
// вычисления
double b_in_decimal=B_deg+B_min/60+B_sec/3600;
double l_in_decimal=L_deg+L_min/60+L_sec/3600;
double eSqr=2*alfa-(alfa*alfa);
double N=a/Math::Sqrt(1-eSqr*(Math::Sin(b_in_decimal))*(Math::Sin(b_in_decimal)));
double X=(N+H)*Math::Cos(b_in_decimal)*Math::Cos(l_in_decimal);
double Y=(N+H)*Math::Cos(b_in_decimal)*Math::Sin(l_in_decimal);
String strX;
String strY;
// преобразование числовых значений координат в строковое представление
strX.Format(MAX_BUFF_SIZE,L"%f",X);
strY.Format(MAX_BUFF_SIZE,L"%f",Y);
// вывод полученых чисел в Label
__pX->SetText(L"X="+strX);__pY->SetText(L"Y="+strY);
__pX->Draw();__pY->Draw();
__pX->Show();__pY->Show();
}
break;
case ID_BUTTON_CLEAR:
{
// реакция на нажатие кнопки Clear
// создаем диалоговое окно с кнопками OK и CANCEL и вопросом - очистить все поля?
MessageBox *pMessageBox = new MessageBox();
pMessageBox->Construct(L"MessageBox", L"Clear all fields?" , MSGBOX_STYLE_OKCANCEL ,9000);
int ModalResult;
pMessageBox->ShowAndWait(ModalResult);
// если ответ положительный,то ...
if (ModalResult==MSGBOX_RESULT_OK) {
// ... очищаем поля ввода и надписи Label
__pB_deg->Clear();
__pB_min->Clear();
__pB_sec->Clear();
__pL_deg->Clear();
__pL_min->Clear();
__pL_sec->Clear();
__pH->Clear();
__pX->SetText(L"X=");__pY->SetText(L"Y=");
}
// удаляем MessageBox
delete pMessageBox;
}
break;
default:
break;
}
}
Внешний вид симулятора с запущенной программой выглядит следующим образом:

Он же с MessageBox:

Вот, кажется, и все. Хочу отметить, что приложение нисколько не претендует на широту охвата и корректность всех формулировок, например, напрочь отсутствует «защита от дурака», поскольку я надеюсь на вменяемость моего знакомого и четверых его коллег, для которых эта программа предназначена.
Совсем недавно поступила ещё одна заявка на разработку для Bada — необходим поиск и отображение на карте ближайших объектов, вроде банкоматов или терминалов пополнения счета. Если эта тема кому-нибудь будет интересна, я с удовольствием поделюсь опытом.
Если у кого-либо возник вопрос, что, собственно, с программой делать дальше, то это подробно освещено в статьях на сайте Bada Developer, я этого касаться не буду, поскольку программирование все-таки тема посложнее.
Исходники приложения доступны здесь.
Если что-то не так я сделал с хостингом, извините, пожалуйста:)
UPD: Простите, забыл добавить — все добавленные поля класса формы (__pB_deg, _pB_min и т.д.) следует инициализировать в конструкторе формы так:
Form1::Form1(void):
__pB_deg(null),__pB_min(null),__pB_sec(null),
__pL_deg(null),__pL_min(null),__pL_sec(null),
__pH(null),
__pX(null),__pY(null)
{
}